├── .gitignore ├── AutoFoldCode.py ├── AutoFoldCode.sublime-commands ├── AutoFoldCode.sublime-settings ├── README.md ├── messages.json ├── messages ├── 1.1.0.txt ├── 1.1.1.txt ├── 1.2.0.txt ├── 1.3.0.txt ├── 1.4.0.txt └── install.txt └── resources └── fold-marker.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /AutoFoldCode.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sublime 3 | import sublime_plugin 4 | import zlib 5 | 6 | ''' 7 | Storage file format (1.2.0+): 8 | 9 | { 10 | "version": 1, 11 | "folds": 12 | { 13 | "": 14 | { 15 | "": 16 | [ 17 | [ 18 | , 19 | 20 | ], 21 | ... 22 | ], 23 | ... 24 | }, 25 | ... 26 | } 27 | } 28 | 29 | Storage file format (pre-1.2.0): 30 | 31 | { 32 | "": 33 | [ 34 | [ 35 | , 36 | 37 | ], 38 | ... 39 | ], 40 | ... 41 | } 42 | ''' 43 | 44 | # File name/path where the plugin data is saved. 45 | __storage_file__ = 'AutoFoldCode.sublime-settings' 46 | __storage_path__ = os.path.join('Packages', 'User', __storage_file__) 47 | 48 | CURRENT_STORAGE_VERSION = 1 49 | MAX_BUFFER_SIZE_DEFAULT = 1000000 50 | 51 | # Called at sublime startup: restore folded regions for each view. 52 | def plugin_loaded(): 53 | for window in sublime.windows(): 54 | for view in window.views(): 55 | view.run_command("auto_fold_code_restore") 56 | 57 | # Listen to changes in views to automatically save code folds. 58 | class AutoFoldCodeListener(sublime_plugin.EventListener): 59 | def on_load_async(self, view): 60 | view.run_command("auto_fold_code_restore") 61 | 62 | def on_post_save_async(self, view): 63 | # Keep only the latest version, since it's guaranteed that on open, the 64 | # saved version of the file is opened. 65 | _save_view_data(view, True) 66 | 67 | # Listening on close events is required to handle hot exit, for whom there is 68 | # no available listener. 69 | def on_close(self, view): 70 | # In this case, we don't clear the previous versions view data, so that on 71 | # open, depending on the previous close being a hot exit or a regular window 72 | # close, the corresponding view data is retrieved. 73 | # 74 | # If a user performs multiple modifications and hot exits, the view data for 75 | # each version is stored. This is acceptable, since the first user initiated 76 | # save will purge the versions and store only the latest. 77 | _save_view_data(view, False) 78 | 79 | def on_text_command(self, view, command_name, args): 80 | if command_name == 'unfold_all' and view.file_name() != None: 81 | _clear_cache(view.file_name()) 82 | 83 | # ------------------- # 84 | # Window Commands # 85 | # ------------------- # 86 | 87 | # Clears all the saved code folds and unfolds all the currently open windows. 88 | class AutoFoldCodeClearAllCommand(sublime_plugin.WindowCommand): 89 | def run(self): 90 | _clear_cache('*') 91 | self.window.run_command('auto_fold_code_unfold_all') 92 | 93 | # Clears the cache for the current view, and unfolds all its regions. 94 | class AutoFoldCodeClearCurrentCommand(sublime_plugin.WindowCommand): 95 | def run(self): 96 | view = self.window.active_view() 97 | if view and view.file_name(): 98 | view.unfold(sublime.Region(0, view.size())) 99 | _clear_cache(view.file_name()) 100 | 101 | def is_enabled(self): 102 | view = self.window.active_view() 103 | return view != None and view.file_name() != None 104 | 105 | # Unfold all code folds in all open files. 106 | class AutoFoldCodeUnfoldAllCommand(sublime_plugin.WindowCommand): 107 | def run(self): 108 | for window in sublime.windows(): 109 | for view in window.views(): 110 | view.unfold(sublime.Region(0, view.size())) 111 | 112 | # ----------------- # 113 | # Text Commands # 114 | # ----------------- # 115 | 116 | class AutoFoldCodeRestoreCommand(sublime_plugin.TextCommand): 117 | def run(self, edit): 118 | file_name = self.view.file_name() 119 | if file_name == None: 120 | return 121 | 122 | # Skip restoring folds if the file size is larger than `max_buffer_size`. 123 | settings = _load_storage_settings(save_on_reset=True) 124 | if self.view.size() > settings.get("max_buffer_size", MAX_BUFFER_SIZE_DEFAULT): 125 | return 126 | 127 | view_content_checksum = _compute_view_content_checksum(self.view) 128 | 129 | # Restore folds 130 | view_folds_data = settings.get("folds").get(file_name, {}) 131 | for a, b in view_folds_data.get(view_content_checksum, []): 132 | self.view.fold(sublime.Region(a, b)) 133 | 134 | # Restore selections 135 | if settings.get("save_selections") == True: 136 | view_selection_data = settings.get("selections").get(file_name, {}) 137 | self.view.selection.clear() 138 | for a, b in view_selection_data.get(view_content_checksum, []): 139 | self.view.selection.add(sublime.Region(a, b)) 140 | 141 | # ----------- # 142 | # Helpers # 143 | # ----------- # 144 | 145 | # Save the folded regions of the view to disk. 146 | def _save_view_data(view, clean_existing_versions): 147 | file_name = view.file_name() 148 | if file_name == None: 149 | return 150 | 151 | # Skip saving data if the file size is larger than `max_buffer_size`. 152 | settings = _load_storage_settings(save_on_reset=False) 153 | if view.size() > settings.get("max_buffer_size", MAX_BUFFER_SIZE_DEFAULT): 154 | return 155 | 156 | def _save_region_data(data_key, regions): 157 | all_data = settings.get(data_key) 158 | if regions: 159 | if clean_existing_versions or not file_name in all_data: 160 | all_data[file_name] = {} 161 | 162 | view_data = all_data.get(file_name) 163 | view_data[view_content_checksum] = regions 164 | else: 165 | all_data.pop(file_name, None) 166 | 167 | settings.set(data_key, all_data) 168 | 169 | view_content_checksum = _compute_view_content_checksum(view) 170 | 171 | # Save folds 172 | fold_regions = [(r.a, r.b) for r in view.folded_regions()] 173 | _save_region_data("folds", fold_regions) 174 | 175 | # Save selections if set 176 | if settings.get("save_selections") == True: 177 | selection_regions = [(r.a, r.b) for r in view.selection] 178 | _save_region_data("selections", selection_regions) 179 | 180 | # Save settings 181 | sublime.save_settings(__storage_file__) 182 | 183 | # Clears the cache. If name is '*', it will clear the whole cache. 184 | # Otherwise, pass in the file_name of the view to clear the view's cache. 185 | def _clear_cache(name): 186 | settings = _load_storage_settings(save_on_reset=False) 187 | 188 | def _clear_cache_section(data_key): 189 | all_data = settings.get(data_key) 190 | file_names_to_delete = [file_name for file_name in all_data if name == '*' or file_name == name] 191 | for file_name in file_names_to_delete: 192 | all_data.pop(file_name) 193 | settings.set(data_key, all_data) 194 | 195 | _clear_cache_section("folds") 196 | _clear_cache_section("selections") 197 | sublime.save_settings(__storage_file__) 198 | 199 | # Loads the settings, resetting the storage file, if the version is old (or broken). 200 | # Returns the settings instance 201 | def _load_storage_settings(save_on_reset): 202 | try: 203 | settings = sublime.load_settings(__storage_file__) 204 | except Exception as e: 205 | print('[AutoFoldCode.] Error loading settings file (file will be reset): ', e) 206 | save_on_reset = True 207 | 208 | if _is_old_storage_version(settings): 209 | settings.set("max_buffer_size", MAX_BUFFER_SIZE_DEFAULT) 210 | settings.set("version", CURRENT_STORAGE_VERSION) 211 | settings.set("folds", {}) 212 | settings.set("selections", {}) 213 | 214 | if save_on_reset: 215 | sublime.save_settings(__storage_file__) 216 | 217 | return settings 218 | 219 | def _is_old_storage_version(settings): 220 | settings_version = settings.get("version", 0) 221 | 222 | # Consider the edge case of a file named "version". 223 | return not isinstance(settings_version, int) or settings_version < CURRENT_STORAGE_VERSION 224 | 225 | # Returns the checksum in Python hex string format. 226 | # 227 | # The view content returned is always the latest version, even when closing 228 | # without saving. 229 | def _compute_view_content_checksum(view): 230 | view_content = view.substr(sublime.Region(0, view.size())) 231 | int_crc32 = zlib.crc32(view_content.encode('utf-8')) 232 | return hex(int_crc32 % (1<<32)) 233 | -------------------------------------------------------------------------------- /AutoFoldCode.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { "caption": "AutoFoldCode: Clear All", "command": "auto_fold_code_clear_all" }, 3 | { "caption": "AutoFoldCode: Clear Current File", "command": "auto_fold_code_clear_current" }, 4 | { "caption": "AutoFoldCode: Unfold All Open Files", "command": "auto_fold_code_unfold_all" }, 5 | { 6 | "caption": "AutoFoldCode: Open Settings", 7 | "command": "edit_settings", 8 | "args": { 9 | "base_file": "${packages}/AutoFoldCode/AutoFoldCode.sublime-settings", 10 | "default": "{\n\t$0\n}\n" 11 | } 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /AutoFoldCode.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | // CONFIGURATION ------------------------------------------------------------- 3 | 4 | // If the size of your view (number of characters in it) exceeds this number, 5 | // then AutoFoldCode will not save code folds (otherwise it risks hanging and 6 | // slowing down your editing). 7 | "max_buffer_size": 1000000, 8 | 9 | // Should AutoFoldCode save and restore your selections inside the editor? 10 | "save_selections": true, 11 | 12 | // INTERNAL STORAGE FOR AUTOFOLDCODE ----------------------------------------- 13 | 14 | // Your code folds are stored in this value. 15 | "folds": {}, 16 | 17 | // Saved selections if enabled. 18 | "selections": {}, 19 | 20 | // ATTENTION: DO NOT EDIT THIS VALUE 21 | // This is used by AutoFoldCode to check the storage version format of your 22 | // folded code regions. If you edit this then you may lose your folds. 23 | "version": 1 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoFoldCode 2 | 3 | A Sublime Text package that: 4 | 5 | * saves/restores folded code regions in files automatically 6 | * saves/restores selections in files automatically 7 | 8 | This plugin makes folded code regions and selections persist - whether you're just opening/closing a file, starting/quitting Sublime Text or rebooting your computer, AutoFoldCode will restore your code folds and selections with no hassle. 9 | 10 | ## Installation 11 | 12 | #### Automatic (preferred) 13 | 14 | 1. Open the Command Palette, and find `Package Control: Install Package`. 15 | 2. Search for `AutoFoldCode` and install. 16 | 17 | #### Manual 18 | 19 | ```bash 20 | # To install, clone this repository into you Sublime Packages directory: 21 | cd /path/to/packages/directory 22 | git clone https://github.com/Vasily-X/AutoFoldCode.git 23 | 24 | # To update to the latest: 25 | git pull origin master 26 | ``` 27 | 28 | ## Usage 29 | 30 | Once installed, AutoFoldCode will automatically begin persisting code folds. 31 | 32 | This package includes some useful commands: 33 | 34 | * `AutoFoldCode: Clear All` 35 | - This command will clear AutoFoldCode's cache, and unfold any folded regions in open views. 36 | * `AutoFoldCode: Clear Current File` 37 | - This command will remove the current file's folded regions from the cache, and unfold all folded regions in the file. 38 | * `AutoFoldCode: Unfold All Open Files` 39 | - This unfolds all open files in all open windows. 40 | - If you want to just unfold just the current file, Sublime Text already includes the `"unfold_all"` command for this. 41 | 42 | ## Configuration 43 | 44 | * `max_buffer_size` 45 | * By default, AutoFoldCode will not save folds in any view whose length exceeds `MAX_BUFFER_SIZE_DEFAULT` characters. You can override this value by supplying a `"max_buffer_size": ` value in your `AutoFoldCode.sublime-settings` file. 46 | * `save_selections` 47 | * You can also disable the feature which saves/restores selections by setting `"save_selections": false` in your `AutoFoldCode.sublime-settings` file. 48 | 49 | ## FAQ 50 | 51 | * "When I start Sublime Text, my code folds/selections aren't restored immediately" 52 | - This is because AutoFoldCode hasn't loaded yet. Once Sublime Text initialises AutoFoldCode, then your folds/selections will be restored. 53 | * "Sometimes AutoFoldCode folds my code in the wrong places" 54 | - ~~This will occur if you have closed Sublime Text, edit the file with another editor, then re-open Sublime Text.~~ This used to occur in older versions of this plugin, but shouldn't anymore since it now uses a hash to verify the contents of the file. 55 | 56 | ## Credits 57 | 58 | Many thanks to the [contributors](https://github.com/Vasily-X/AutoFoldCode/graphs/contributors)! 59 | And of course, thanks to the great developers at [Sublime Text](http://sublimetext.com/). 60 | 61 | ## License 62 | 63 | [MIT](./LICENSE) 64 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.0": "messages/1.1.0.txt", 3 | "1.1.1": "messages/1.1.1.txt", 4 | "1.2.0": "messages/1.2.0.txt", 5 | "1.3.0": "messages/1.3.0.txt", 6 | "1.4.0": "messages/1.4.0.txt", 7 | "install": "messages/install.txt" 8 | } 9 | -------------------------------------------------------------------------------- /messages/1.1.0.txt: -------------------------------------------------------------------------------- 1 | This release includes support for some new commands, as well as various bug fixes and improvements. 2 | Please see the README at https://github.com/RIDE-2DAY/AutoFoldCode for the latest updates. 3 | 4 | Thanks for using AutoFoldCode! 5 | 6 | Changelog: 7 | 8 | * feat: add "AutoFoldCode: Clear Current File" command 9 | * feat: add "AutoFoldCode: Unfold All Open Files" command 10 | * feat: when running "AutoFoldCode: Clear All" this unfolds all the open views as well 11 | * fix: if there were no folded regions, then the cache wouldn't update 12 | * fix: errors now handled when there are no active views 13 | * fix: errors handled when starting plugin with no active views 14 | * fix: cache no longer contains empty entries 15 | * improvements to code quality and structure 16 | -------------------------------------------------------------------------------- /messages/1.1.1.txt: -------------------------------------------------------------------------------- 1 | See the README at https://github.com/RIDE-2DAY/AutoFoldCode for the latest updates. 2 | Thanks for using AutoFoldCode! 3 | 4 | Changelog: 5 | 6 | * fix: closing a scratch view would trigger an exception 7 | -------------------------------------------------------------------------------- /messages/1.2.0.txt: -------------------------------------------------------------------------------- 1 | PLEASE NOTE: 2 | 3 | In order to fully support hot exits in Sublime projects as well as saving individual files, we've pushed a change to the storage file format. This means that as of this update you'll lose your current folded regions (sorry) but we've decided to go ahead and do this since it will make the plugin perform better for everyone. 4 | 5 | Changelog: 6 | 7 | * add: change format of storage file (where all code folds are stored) 8 | * fix: properly handle saving folds on hot exit (as much as we can) 9 | * fix: properly handle saving folds when files are saved 10 | 11 | See the README at https://github.com/RIDE-2DAY/AutoFoldCode for the latest updates. 12 | Thanks for using AutoFoldCode! 13 | -------------------------------------------------------------------------------- /messages/1.3.0.txt: -------------------------------------------------------------------------------- 1 | # 1.3.0 2 | 3 | * add: a configuration option `max_buffer_size` 4 | - AutoFoldCode will no longer save folds in files that are larger than this option. You may configure this value to whatever you'd like, however AutoFoldCode will default to a conservative value in order to not slow down your editor. 5 | 6 | # 1.2.0 7 | 8 | PLEASE NOTE: 9 | 10 | In order to fully support hot exits in Sublime projects as well as saving individual files, we've pushed a change to the storage file format. This means that as of this update you'll lose your current folded regions (sorry) but we've decided to go ahead and do this since it will make the plugin perform better for everyone. 11 | 12 | Changelog: 13 | 14 | * add: change format of storage file (where all code folds are stored) 15 | * fix: properly handle saving folds on hot exit (as much as we can) 16 | * fix: properly handle saving folds when files are saved 17 | 18 | See the README at https://github.com/RIDE-2DAY/AutoFoldCode for the latest updates. 19 | Thanks for using AutoFoldCode! 20 | -------------------------------------------------------------------------------- /messages/1.4.0.txt: -------------------------------------------------------------------------------- 1 | # 1.4.0 2 | 3 | Changes: 4 | 5 | * Feature: AutoFoldCode now remembers your selections inside each file! 6 | This means that when you close and re-open a file, your selections will be restored. 7 | This behaviour can be disabled with the `save_selections: ` setting. 8 | -------------------------------------------------------------------------------- /messages/install.txt: -------------------------------------------------------------------------------- 1 | ___ __ ______ __ ________ __ 2 | / | __ __/ /_____ / ____/___ / /___/ / ____/___ ____/ /__ 3 | / /| |/ / / / __/ __ \/ /_ / __ \/ / __ / / / __ \/ __ / _ \ 4 | / ___ / /_/ / /_/ /_/ / __/ / /_/ / / /_/ / /___/ /_/ / /_/ / __/ 5 | /_/ |_\__,_/\__/\____/_/ \____/_/\__,_/\____/\____/\__,_/\___/ 6 | 7 | =================================================================== 8 | 9 | Thanks for installing AutoFoldCode! This package saves/restores folded code regions in files automatically. 10 | 11 | Remember, you can clear AutoFoldCode's cache by selecting the "AutoFoldCode: Clear" command from the Command Palette. 12 | 13 | For more information, see: https://github.com/RIDE-2DAY/AutoFoldCode 14 | -------------------------------------------------------------------------------- /resources/fold-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vasily-X/AutoFoldCode/47d1a7e64f560a5b3b3abb79ceb7912bf2605cab/resources/fold-marker.png --------------------------------------------------------------------------------