├── Base64 Fold.sublime-settings
├── test.html
├── README.md
├── test.css
└── Base64Fold.py
/Base64 Fold.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | // This settings file will be overwritten on package update. User settings
3 | // should be placed in Packages/User/Preferences.sublime-settings, which
4 | // can be opened from the "Preferences > Settings - User" menu.
5 |
6 | // Fold Base64 code in any file when set to true
7 | "base64fold_any_file": false,
8 |
9 | // When "base64fold_any_file" is set to false, or is omitted, fold Base64
10 | // code in files that match one of the file extensions listed here.
11 | // If this list is set to empty (like this: []) with "base64fold_any_file"
12 | // not being true, no Base64 code will be folded.
13 | // Note: File extension should NOT begin with "." i.e. "css" not ".css"
14 | "base64fold_file_extensions":
15 | [
16 | "css","less","sass","scss",
17 | "htm","html"
18 | ],
19 |
20 | // Fold all data URIs (e.g. UTF8-encoded SVG embeds)
21 | "base64fold_all_uris": true
22 | }
23 |
--------------------------------------------------------------------------------
/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Base64 Fold
2 | ===========
3 |
4 | Base64-encoded Data URIs are notoriously long and look ugly in text editors:
5 |
6 | 
7 |
8 | Since you never edit these embeds, they are just sitting there, wasting space and irritating you by going beyond your screen width or wrapping to many lines.
9 | This little plug-in makes use of Sublime Text folding to hide them:
10 |
11 | 
12 |
13 | Problem solved: the URI is still there, but it's neatly folded and can be unfolded for inspection.
14 |
15 | Installation
16 | ------------
17 |
18 | You can install Base64 Fold through [Sublime Package Control](http://wbond.net/sublime_packages/package_control).
19 |
20 | Don't forget about these default Sublime key shortcuts:
21 |
22 | Fold: ```ctrl+shift+]```
23 |
24 | Unfold: ```ctrl+shift+[```
25 |
26 | Settings
27 | --------
28 |
29 | By default, only Base64 code in CSS and HTML files (recognized by file extensions) will be folded. You can set it to fold in any file, or limit to specific file extensions, by adding your settings to user preferences file, which can be opened from menu `Preferences > Settings - User`.
30 |
31 | For details, see `Packages/Base64 Fold/Base64 Fold.sublime-settings`, or the latest version [here](Base64 Fold.sublime-settings).
32 |
--------------------------------------------------------------------------------
/test.css:
--------------------------------------------------------------------------------
1 | /* normal base64 */
2 | li {
3 | list-style-image: url('data:image/png;base64,Y2VsbGFyIGRvb3I=');
4 | }
5 |
6 | /* base64 incorrect size */
7 | li {
8 | list-style-image: url('data:image/png;base64,Y2VsbGFyIGRvb3I');
9 | }
10 |
11 | /* svg in utf8 */
12 | li {
13 | list-style-image: url('data:image/svg+xml;utf8,');
14 | }
15 |
16 | /* multiline svg in utf8 */
17 | li {
18 | list-style-image: url('data:image/svg+xml;utf8,');
20 | }
21 |
22 | /* should not be folded, false positive if it is */
23 | li {
24 | list-style-image: rgba(0, 0, 0, .5);
25 | }
26 |
27 | /* multiline base64 with whitespace */
28 | span {
29 | padding-left: 20px;
30 | background:white url('data:image/png;base64,iVBORw0KGgoAA\
31 | AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAA BlBMVEUAAAD///+l2Z/dAAAAM0l\
32 | EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\
33 | P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC') no-repeat scroll left top;
34 | }
35 | /* Backslashes at end of line - to continue character string
36 | at new line. */
37 |
38 | /* multiline base64 with whitespace, incorrect size */
39 | span {
40 | padding-left: 20px;
41 | background:white url('data:image/png;base64,iVBORw0KGgoAA\
42 | AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAA BlBMVEUAAAD///+l2Z/dAAAAM0l\
43 | EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\
44 | P9/AFGGFyjOXZtQAAAAAElFTkSuQmCCk') no-repeat scroll left top;
45 | }
46 |
47 | /* multiline base64 with whitespace and no indentation */
48 | span {
49 | padding-left: 20px;
50 | background:white url('data:image/png;base64,iVBORw0KGgoAA\
51 | AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAA BlBMVEUAAAD///+l2Z/dAAAAM0l\
52 | EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6\
53 | P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC') no-repeat scroll left top;
54 | }
55 |
--------------------------------------------------------------------------------
/Base64Fold.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sublime, sublime_plugin
3 |
4 | class FoldBase64Command(sublime_plugin.TextCommand):
5 | def run(self, edit, fold_all_uris):
6 |
7 | whitespace = r'(?:\\?\n|[\t ])+'
8 | # Only fold Base64 code that has a valid length - ignoring whitespace
9 | base64_filter = lambda region: len(re.sub(whitespace, '', self.view.substr(region))) % 4 == 0
10 | if fold_all_uris:
11 | # fold all URIs if the option is enabled
12 | regexp = r'url\(([\'"])?[^;]+;([^,]+),([^\n\\]+?(?:\\\n[^\n\\]*?)*?)(?=\1?\))'
13 | match_index = 3 # third group
14 | base64_index = 2 # second group
15 | self.fold_by_pattern(regexp, base64_index, match_index, base64_filter)
16 |
17 | # fold the base64 URIs
18 | base64 = r'[\w\d+/]{2,}'
19 | # Base64 decodes each 4 encoded characters into 3 octects. The padding '='
20 | # character might appear 1 or 2 times only at the end of the Base64 code,
21 | # and only if there are fewer than 3 octects to be decoded. We don't need to
22 | # match what (["');], etc) is after the code, as they are not being folded.
23 | regexp = r'(base64),(' + base64 + r'(?:' + whitespace + base64 + r')*={0,2})'
24 | match_index = 2 # second group
25 | base64_index = 1 # first group
26 |
27 | self.fold_by_pattern(regexp, base64_index, match_index, base64_filter)
28 |
29 | def fold_by_pattern(self, regexp, base64_index, match_index, base64_region_filter):
30 | view_text = self.view.substr(sublime.Region(0, self.view.size()))
31 |
32 | def get_region_from_group(match, group_index):
33 | start = match.start(group_index)
34 | end = start + len(match.group(group_index))
35 | return sublime.Region(start, end)
36 |
37 | pattern = re.compile(regexp)
38 | for match in pattern.finditer(view_text):
39 | is_base64 = self.view.substr(get_region_from_group(match, base64_index)) == 'base64'
40 | fold_region = get_region_from_group(match, match_index)
41 | if not is_base64 or base64_region_filter(fold_region):
42 | self.view.fold(fold_region)
43 |
44 | class Base64Fold(sublime_plugin.EventListener):
45 | def init_(self):
46 | active_view = sublime.active_window().active_view()
47 | self.on_load(active_view)
48 | for window in sublime.windows():
49 | self.on_load(window.active_view())
50 |
51 | def load_settings(self, view):
52 | fold_any_file = False
53 | fold_file_extensions = []
54 | fold_all_uris = False
55 | scope_package = 'package'
56 | scope_sublime = 'sublime'
57 | settings = {
58 | scope_package: sublime.load_settings('Base64 Fold.sublime-settings'),
59 | scope_sublime: view.settings()
60 | }
61 | # Settings override order:
62 | # User/Preferences > User/Base64 Fold > Base64 Fold/Base64 Fold
63 | for scope in [scope_package, scope_sublime]:
64 | if settings[scope]:
65 | fold_any_file = settings[scope].get('base64fold_any_file', fold_any_file)
66 | fold_file_extensions = settings[scope].get(
67 | 'base64fold_file_extensions',
68 | fold_file_extensions
69 | )
70 | fold_all_uris = settings[scope].get('base64fold_all_uris', fold_all_uris)
71 |
72 | return {
73 | 'fold_any_file': fold_any_file,
74 | 'fold_file_extensions': fold_file_extensions,
75 | 'fold_all_uris': fold_all_uris
76 | }
77 |
78 | def on_load(self, view):
79 | active_view = sublime.active_window().active_view()
80 | self.settings = self.load_settings(active_view)
81 | self.fold(view)
82 |
83 | def on_pre_save(self, view):
84 | active_view = sublime.active_window().active_view()
85 | self.settings = self.load_settings(active_view)
86 | self.fold(view)
87 |
88 | def fold(self, view):
89 | gotta_fold = False
90 | # to fold or not to fold
91 | if self.settings.get('fold_any_file'):
92 | gotta_fold = True
93 | elif len(self.settings.get('fold_file_extensions')) > 0:
94 | file_name = view.file_name()
95 | if file_name:
96 | match = re.search('(?<=\.)[0-9a-z]+$', file_name, re.IGNORECASE)
97 | if match:
98 | extension = match.group(0)
99 | if extension in self.settings.get('fold_file_extensions'):
100 | gotta_fold = True
101 |
102 | if gotta_fold:
103 | view.run_command('fold_base64', { 'fold_all_uris': self.settings.get('fold_all_uris') })
104 |
105 | def plugin_loaded():
106 | Base64Fold().init_()
107 |
--------------------------------------------------------------------------------