├── 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 | Red dot 9 | 10 | 11 | Not a red dot 12 | 13 | 14 | Red dot 17 | 18 | 19 | Not a red dot 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 | ![](http://i.imgur.com/r7wHI.png) 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 | ![](http://i.imgur.com/YcTH6.png) 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 | --------------------------------------------------------------------------------