├── Default (Linux).sublime-keymap ├── Default (OSX).sublime-keymap ├── Default (Windows).sublime-keymap ├── Default.sublime-commands ├── LICENSE ├── Main.sublime-menu ├── README.md ├── fav.sublime-syntax ├── favorites.py ├── favorites.sublime-settings ├── images ├── commands.png ├── favorites.gif └── global_vs_project.png ├── messages.json └── messages ├── 1.0.1.txt ├── 1.0.2.txt ├── 1.0.3.txt ├── 1.0.4.txt ├── 1.0.5.txt ├── 1.0.7.txt └── install.txt /Default (Linux).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | ] 3 | -------------------------------------------------------------------------------- /Default (OSX).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | ] 3 | -------------------------------------------------------------------------------- /Default (Windows).sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | ] 4 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Favorites: Toggle Visibility", 4 | "command": "show_favorites" 5 | } 6 | , 7 | { 8 | "caption": "Favorites: Add active view (global)", 9 | "command": "add_favorites" 10 | } 11 | , 12 | { 13 | "caption": "Favorites: Add active view (project's)", 14 | "command": "add_proj_favorites" 15 | } 16 | , 17 | { 18 | "caption": "Favorites: Edit favorites (global)", 19 | "command": "edit_favorites_file" 20 | } 21 | , 22 | { 23 | "caption": "Favorites: Edit favorites (project's)", 24 | "command": "edit_proj_favorites_file" 25 | } 26 | , 27 | { 28 | "caption": "Favorites: Open all (global)", 29 | "command": "openall_favorites" 30 | }, 31 | { 32 | "caption": "Favorites: Open all (project's)", 33 | "command": "openall_proj_favorites" 34 | } 35 | , 36 | { 37 | "caption": "Favorites: Remove all (global)", 38 | "command": "remove_favorites_file" 39 | }, 40 | { 41 | "caption": "Favorites: Remove all (project's)", 42 | "command": "remove_proj_favorites_file" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Oleg Shilo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "Preferences", 4 | "mnemonic": "n", 5 | "id": "preferences", 6 | "children": [ 7 | { 8 | "caption": "Package Settings", 9 | "mnemonic": "P", 10 | "id": "package-settings", 11 | "children": [ 12 | { 13 | "caption": "Favorites", 14 | "children": [ 15 | { 16 | "command": "open_file", 17 | "args": { 18 | "file": "${packages}/Favorites/favorites.sublime-settings" 19 | }, 20 | "caption": "Settings – Default" 21 | }, 22 | { 23 | "command": "open_file", 24 | "args": { 25 | "file": "${packages}/User/favorites.sublime-settings" 26 | }, 27 | "caption": "Settings – User" 28 | } 29 | ] 30 | } 31 | ] 32 | } 33 | ] 34 | } 35 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sublime Favorites 2 | 3 | A plugin for displaying Favorites (list of frequently used documents) in the [Sublime Text 3](http://sublimetext.com "Sublime Text") editor. 4 | 5 | While plugin functionality overlaps with some other similar ST3 plugins, it has a very strong distinction - it offers visual management of the Favorites' items. 6 | 7 | ## Installation 8 | 9 | Note the plugin was developed and tested against ST3 but not ST2. 10 | 11 | *__Package Control__* 12 | 13 | _Not available yet: waiting for Sublime team approval._
14 | You can install the pluging [Package Control](https://packagecontrol.io/packages/Favorites). 15 | 16 | *__Manual__* 17 | 18 | * Remove the package, if installed, using Package Control. 19 | * Add a repository: `https://github.com/oleg-shilo/sublime-favorites.git` 20 | * Install `sublime-favorites` with Package Control. 21 | 22 | You can also install the plugin by cloning `sublime-favorites` repository into your Packages folder or manually placing the download package there. 23 | 24 | ## Usage 25 | The plugin uses a dedicated view group on right side (favorites panel) to mimic a "side bar" containing the list of "favorite documents". 26 | 27 | All visual elements in the panel can be double-clicked to invoke the default command associated with the element. Alternatively user can hover the mouse cursor over the item to trigger an interactive tooltip, which allows invoking multiple commands associated with the visual element. 28 | 29 | To start working with Favorites just make the favorites panel visible (e.g. press `cmd+shift+p` and type/select `Favorites: Toggle Visibility`). 30 | 31 | Alternatively, you can configure custom binding instead: 32 | 33 | _Open keymap menu_ 34 | 35 | Preferences > Key Bindings > Default (...).sublime-keymap - User 36 | 37 | _Add mapping:_ 38 | 39 | ``` 40 | { "keys": ["alt+f", "alt+f"], "command": "show_favorites" } 41 | ``` 42 | 43 | ![](images/favorites.gif) 44 | 45 | ## Functionality 46 | 47 | 1. *Toggle Visibility* 48 | Show/Hide favorites panel either with *Command Palette* or by the configured shortcut (e.g. [alt+f, alt+f]). 49 | 50 | 2. *Add active document to favorites* 51 | Double-click the `Add` command at the top of the panel. 52 | Alternatively, hover over `Add` command at the top of the panel and then click `Add active view...` on the tooltip. 53 | 54 | 3. Remove the item (document) from the favorites. 55 | Hover over the document item in the panel and then click `Remove from...` on the tooltip. 56 | 57 | 4. *Open the item (document)* 58 | Double-click the document item in the panel. 59 | Alternatively, hover the document item in the panel and then click `Open...` on the tooltip. 60 | 61 | 5. *Edit Favorites data file directly* 62 | Double-click the `Edit` command at the top of the panel. 63 | Alternatively, hover over `Edit` command at the top of the panel and then click `Edit...` on the tooltip. 64 | 65 | 6. *Project specific favorites* 66 | The plugin maintains a separate list of favorites per project. It is merged with the global favorites if the project file is loaded into ST. The project favorites are always placed in the bottom section of the favorites panel. All per-project items can be dealt with (e.g. Add, Remove, edit) the same ways as global ones. 67 | 68 | 7. *File aliases* 69 | By default the items in the favorites panel appears with their file name listed. However if a custom name is more beneficial a file alias can be assigned to the item in the data file via "Edit favorites" command. 70 | 71 | The format is simple: `|`: 72 | ``` 73 | shelloverlay|Q:\Extras\Utils\TortoiseIconOverlays.cs 74 | ``` 75 | 76 | ![](images/global_vs_project.png) 77 | 78 | 79 | Practically all functionality available via favorites panel is also available via *Command Palette*: 80 | ![](images/commands.png) 81 | 82 | ## Settings 83 | 84 | You can also configure plugin to: 85 | 1. Hide the group on closing the panel when it is the only view in the group. 86 | 2. Always place favorites panel in the individual most-right column. Only up to 4 columns layout is supported.is alse available 87 | 3. Use [Favorite Files](https://github.com/facelessuser/FavoriteFiles) plugin data file.
88 | The integration is limited to reading `Favorite Files` data file, flattening it and allowing to open files on double-click on the item in the Favorites panel. 89 | 90 | _favorites.sublime-settings_ 91 | 92 | ```js 93 | { 94 | "close_empty_group_on_closing_panel": true, 95 | "show_in_new_group": true, 96 | "favorite_files_integration_enabled": false 97 | } 98 | ``` 99 | -------------------------------------------------------------------------------- /fav.sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | # See http://www.sublimetext.com/docs/3/syntax.html 4 | name: Fav 5 | file_extensions: 6 | - ec 7 | - fav 8 | scope: text.html.favorites 9 | contexts: 10 | main: 11 | - match: ([#]{1,6})(.*)+(:)(\d+) 12 | captures: 13 | 1: string.other.link.title.markdown 14 | 2: punctuation.definition.constant.markdown 15 | 3: entity.name.section 16 | 4: entity.name.section 17 | 18 | - match: '\b(Add|Edit|Refresh|Open all)\b' 19 | scope: keyword.control.example-c -------------------------------------------------------------------------------- /favorites.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import codecs 4 | import os 5 | import shutil 6 | import sys 7 | from os import path 8 | from sublime import Region 9 | import socket 10 | import subprocess 11 | import errno 12 | 13 | # version = 1.0.8 14 | 15 | if sys.version_info < (3, 3): 16 | raise RuntimeError('Favorites works with Sublime Text 3 only.') 17 | 18 | # ============================================================ 19 | # Packages/.sublime-package/favorites.py 20 | package_name = path.basename(path.dirname(__file__)).replace('.sublime-package', '') 21 | 22 | fav_syntax = 'Packages/'+package_name+'/fav.tmLanguage' 23 | plugin_name = 'favorites' 24 | panel_name = 'Favorites' 25 | 26 | def using_Favorite_Files_data(): 27 | return sublime.load_settings(plugin_name+".sublime-settings").get('favorite_files_integration_enabled', True) 28 | 29 | # ----------------- 30 | def favorites_data_path(per_project): 31 | file = None 32 | 33 | if per_project: 34 | project = sublime.active_window().project_file_name() 35 | if project: 36 | file = project + '.favorites' 37 | else: 38 | file = os.path.join(sublime.packages_path(), 'User', 'favorites.txt') 39 | 40 | if file and not path.exists(file): 41 | with open(file, "w") as f: 42 | f.write('') 43 | 44 | return file 45 | # ------------------------- 46 | def show_integration_constrains_message(): 47 | sublime.error_message('Since you are using "Favorite Files" plugin integration '+ 48 | 'you have to use "Favorite Files" commands via command palette '+ 49 | 'to add, remove or edit the file list.') 50 | 51 | # ------------------------- 52 | def get_favorite_files_data(): 53 | """ 54 | Integration with Favorit_Files plugin 55 | It goes only as far as reading its data file, flattening it and allowing to 56 | open files on double-click on the item in the Favorites panel 57 | """ 58 | import json 59 | 60 | file_name = os.path.join(sublime.packages_path(), 'User', 'favorite_files_list.json') 61 | 62 | with open(file_name) as data_file: 63 | data = json.load(data_file) 64 | 65 | result = [] 66 | for f in data["files"]: 67 | result.append(f) 68 | 69 | for name in data["groups"].keys(): 70 | for f in data["groups"][name]: 71 | result.append(f) 72 | 73 | return result 74 | # ------------------------- 75 | def get_favorites(per_project): 76 | if using_Favorite_Files_data(): 77 | return get_favorite_files_data() 78 | else: 79 | file = favorites_data_path(per_project) 80 | lines = [] 81 | if file and os.path.exists(file): 82 | with codecs.open(file, "r", encoding='utf8') as f: 83 | content = f.read().strip() 84 | if content != '': 85 | lines = content.split('\n') 86 | 87 | return [x.strip() for x in lines] 88 | # ------------------------- 89 | def set_favorites(lines, per_project): 90 | if using_Favorite_Files_data(): 91 | show_integration_constrains_message() 92 | else: 93 | file = favorites_data_path(per_project) 94 | with codecs.open(file, "w", encoding='utf8') as f: 95 | f.write('\n'.join(lines)) 96 | # ------------------------- 97 | def del_favorites(lines, per_project): 98 | if not using_Favorite_Files_data(): 99 | file = favorites_data_path(per_project) 100 | with codecs.open(file, "w", encoding='utf8') as f: 101 | f.write('\n'.join(lines)) 102 | # ------------------------- 103 | def get_favorite_path(index): 104 | # print(index) 105 | lines = get_favorites(False) 106 | p_lines = get_favorites(True) 107 | 108 | if index < len(lines): 109 | return extract_path(lines[index]); 110 | 111 | index -= len(lines) 112 | index -= items_offset # project favorites will have extra two lines in the header (items_offset) 113 | if index >= 0 and index < len(p_lines) : 114 | # print(index, '/',len(p_lines)) 115 | return extract_path(p_lines[index]); 116 | 117 | return None 118 | # ------------------------- 119 | def open_favorite_path(index): 120 | file = get_favorite_path(index) 121 | if file: 122 | focus_prev_view_group() 123 | open_path(file) 124 | # ------------------------- 125 | def add_active_view(arg=None, per_project=False): 126 | if arg: 127 | file = arg 128 | else: 129 | file = sublime.active_window().active_view().file_name() 130 | 131 | lines = get_favorites(per_project) 132 | if not file in lines: 133 | lines.append(file) 134 | set_favorites(lines, per_project) 135 | 136 | refresh_favorites() 137 | # ------------------------- 138 | def focus_prev_view_group(): 139 | try: 140 | if favorites_listener.last_active_view: 141 | if favorites_listener.last_active_view == get_panel_view(): 142 | group, _ = sublime.active_window().get_view_index(favorites_listener.last_active_view) 143 | else: 144 | group = 0 145 | sublime.active_window().focus_group(group) 146 | except: 147 | pass 148 | # ------------------------- 149 | def remove_from_favorites(arg, per_project): 150 | file = arg 151 | lines = [] 152 | for file in get_favorites(per_project): 153 | if file != arg: 154 | lines.append(file) 155 | set_favorites(lines, per_project) 156 | refresh_favorites() 157 | # ------------------------- 158 | def remove_all_from_favorites(per_project): 159 | lines = [] 160 | set_favorites(lines, per_project) 161 | refresh_favorites() 162 | # ------------------------- 163 | def edit_favorites(per_project): 164 | if using_Favorite_Files_data(): 165 | show_integration_constrains_message() 166 | else: 167 | focus_prev_view_group() 168 | open_path(favorites_data_path(per_project)) 169 | # ------------------------- 170 | def refresh_favorites(): 171 | panel_view = get_panel_view() 172 | if panel_view: 173 | panel_view.run_command(plugin_name+'_generator') 174 | # ------------------------- 175 | def open_all_favorites(per_project): 176 | 177 | for item in get_favorites(per_project): 178 | file = extract_path(item) 179 | view = sublime.active_window().find_open_file(file) 180 | if not view: 181 | view = sublime.active_window().open_file(file) 182 | 183 | if view: 184 | sublime.active_window().focus_view(view) 185 | # ------------------------- 186 | def open_path(file): 187 | view = sublime.active_window().find_open_file(file) 188 | if not view: 189 | view = sublime.active_window().open_file(file) 190 | if view: 191 | sublime.active_window().focus_view(view) 192 | # ------------------------- 193 | def extract_title(text): 194 | if text: 195 | parts = text.split('|') 196 | if len(parts) == 1: 197 | return path.basename(text.strip()) 198 | else: 199 | return parts[0] 200 | return '' 201 | # ------------------------- 202 | def extract_path(text): 203 | if text: 204 | parts = text.split('|') 205 | if len(parts) > 1: 206 | return parts[len(parts)-1].strip() 207 | else: 208 | return text 209 | # ------------------------- 210 | tooltip_template = """ 211 | 212 | 216 | %s 217 | 218 | """ 219 | # ------------------------- 220 | class show_favorites(sublime_plugin.TextCommand): 221 | def run(self, edit): 222 | show_panel.run(self, edit); 223 | # ------------------------- 224 | # ====================================================== 225 | class add_favorites(sublime_plugin.TextCommand): 226 | # ----------------- 227 | def run(self, edit): 228 | add_favorites.do(self.view, False) 229 | # ----------------- 230 | def do(view, per_project): 231 | file = view.file_name() 232 | if file: 233 | add_active_view(file, per_project) 234 | else: 235 | sublime.error_message('You can add to favorites only view that has been saved.') 236 | # ------------------------- 237 | class add_proj_favorites(sublime_plugin.TextCommand): 238 | # ----------------- 239 | def is_enabled(self): 240 | return self.view.window().project_file_name() != None 241 | # ----------------- 242 | def run(self, edit): 243 | edit_favorites(True) 244 | # ------------------------- 245 | class edit_favorites_file(sublime_plugin.TextCommand): 246 | # ----------------- 247 | def run(self, edit): 248 | edit_favorites(False) 249 | # ------------------------- 250 | class edit_proj_favorites_file(sublime_plugin.TextCommand): 251 | # ----------------- 252 | def is_enabled(self): 253 | return self.view.window().project_file_name() != None 254 | # ----------------- 255 | def run(self, edit): 256 | edit_favorites(True) 257 | # ------------------------- 258 | class remove_favorites_file(sublime_plugin.TextCommand): 259 | # ----------------- 260 | def run(self, edit): 261 | remove_all_from_favorites(False) 262 | # ------------------------- 263 | class remove_proj_favorites_file(sublime_plugin.TextCommand): 264 | # ----------------- 265 | def is_enabled(self): 266 | return self.view.window().project_file_name() != None 267 | # ----------------- 268 | def run(self, edit): 269 | remove_all_from_favorites(True) 270 | # ------------------------- 271 | class openall_favorites(sublime_plugin.TextCommand): 272 | # ----------------- 273 | def run(self, edit): 274 | open_all_favorites(False) 275 | # ------------------------- 276 | class openall_proj_favorites(sublime_plugin.TextCommand): 277 | # ----------------- 278 | def is_enabled(self): 279 | return self.view.window().project_file_name() != None 280 | # ----------------- 281 | def run(self, edit): 282 | open_all_favorites(True) 283 | # ====================================================== 284 | 285 | # ------------------------- 286 | items_offset = 2 287 | # ----------------- 288 | class favorites_generator(sublime_plugin.TextCommand): 289 | def run(self, edit): 290 | panel_view = self.view 291 | 292 | lines = get_favorites(False) 293 | 294 | map = "Add Edit Open all" 295 | map += "\n-----------------------" 296 | for line in lines: 297 | map += "\n"+extract_title(line) 298 | 299 | project = sublime.active_window().project_file_name() 300 | if project: 301 | map += '\n\n--- "'+path.basename(project).replace('.sublime-project', '" project')+' ---' 302 | p_lines = get_favorites(True) 303 | for line in p_lines: 304 | map += '\n' + extract_title(line) 305 | 306 | map_syntax = fav_syntax 307 | 308 | panel_view.set_read_only(False) 309 | 310 | all_text = sublime.Region(0, panel_view.size()) 311 | panel_view.replace(edit, all_text, map) 312 | panel_view.set_scratch(True) 313 | 314 | panel_view.assign_syntax(map_syntax) 315 | panel_view.set_read_only(True) 316 | # ----------------- 317 | class favorites_listener(sublime_plugin.EventListener): 318 | # ----------------- 319 | last_active_view = None 320 | # ----------------- 321 | def on_activated(self, view): 322 | if view != get_panel_view() and view.file_name(): 323 | favorites_listener.last_active_view = view 324 | # ----------------- 325 | def on_hover(self, view, point, hover_zone): 326 | if view.file_name() == panel_file(): 327 | row, col = view.rowcol(point) 328 | if row == 0: 329 | 330 | project = sublime.active_window().project_file_name() 331 | 332 | callback = None 333 | html = "" 334 | command = view.substr(view.word(point)).lower() 335 | if command == "add": 336 | def add(arg): 337 | view.hide_popup() 338 | per_project = (arg == 'add_to_proj') 339 | add_active_view(None, per_project) 340 | 341 | file = sublime.active_window().active_view().file_name() 342 | link_open = '' 343 | if project: 344 | # link_open = file+'
' 345 | link_open += 'Add active view ('+os.path.basename(file)+')
' 346 | link_open += '  to favorites
' 347 | link_open += '  to "'+os.path.basename(project).replace('.sublime-project', '')+'" project favorites' 348 | else: 349 | link_open += 'Add active view ('+os.path.basename(file)+') to favorites' 350 | 351 | 352 | html = tooltip_template % (link_open) 353 | callback = add 354 | 355 | elif command == "edit": 356 | def refresh(arg): 357 | view.hide_popup() 358 | per_project = (arg == 'edit.proj') 359 | edit_favorites(per_project) 360 | 361 | html_content = 'Edit favorites file' 362 | if project: 363 | html_content += '
Edit "'+os.path.basename(project).replace('.sublime-project', '')+'" project favorites file' 364 | 365 | html = tooltip_template % (html_content) 366 | 367 | callback = refresh 368 | 369 | elif command == "Open" or "all" : 370 | def open(arg): 371 | view.hide_popup() 372 | per_project = (arg == 'proj') 373 | open_all_favorites(per_project) 374 | 375 | html_content = 'Open all favorites files' 376 | if project: 377 | html_content += '
Open all "'+os.path.basename(project).replace('.sublime-project', '')+'" project favorites files' 378 | 379 | html = tooltip_template % (html_content) 380 | 381 | callback = open 382 | 383 | elif command == "refresh": 384 | def refresh(arg): 385 | view.hide_popup() 386 | refresh_favorites() 387 | html = tooltip_template % ('Refresh favorites') 388 | callback = refresh 389 | 390 | else: 391 | return 392 | 393 | view.show_popup(html, location=point, flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY, max_width=600, on_navigate=callback) 394 | 395 | else: 396 | index = row-items_offset 397 | global_list = get_favorites(False) 398 | per_project = (index >= len(global_list)) 399 | 400 | file = get_favorite_path(index) 401 | 402 | if file: 403 | link_open = file+'
' 404 | link_open += '  Open in active window
' 405 | link_open += '  Remove from the' 406 | if per_project: 407 | link_open += " project's favorites" 408 | else: 409 | link_open += ' favorites' 410 | 411 | html = tooltip_template % (link_open) 412 | 413 | def open(arg): 414 | view.hide_popup() 415 | if arg.startswith('remove.'): 416 | remove_from_favorites(file, per_project) 417 | else: 418 | open_path(arg) 419 | 420 | view.show_popup(html, location=point, flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY, max_width=600, on_navigate=open) 421 | # ----------------- 422 | def on_post_text_command(self, view, command_name, args): 423 | # process double-click on code panel view` 424 | per_project = sublime.active_window().project_file_name()!= None 425 | if view.file_name() == panel_file(): 426 | if command_name == 'drag_select' and 'by' in args.keys() and args['by'] == 'words': 427 | point = view.sel()[0].begin() 428 | row, col = view.rowcol(point) 429 | view.sel().clear() 430 | 431 | if row > 1: 432 | open_favorite_path(row - items_offset) 433 | 434 | elif row == 0: 435 | command = view.substr(view.word(point)).lower() 436 | if command == 'add': 437 | last_view = favorites_listener.last_active_view 438 | if last_view != view: 439 | add_active_view(last_view.file_name()) 440 | 441 | elif command == 'edit': 442 | edit_favorites(per_project) 443 | elif command == 'refresh': 444 | refresh_favorites() 445 | 446 | 447 | ######################################################################################################################### 448 | # 449 | # ============================================================ 450 | # Layout management 451 | # ============================================================ 452 | def settings(): 453 | return sublime.load_settings(plugin_name+".sublime-settings") 454 | # ----------------- 455 | def panel_file(): 456 | 457 | plugin_dir = '' 458 | 459 | if hasattr(sublime, 'cache_path'): 460 | plugin_dir = sublime.cache_path() 461 | else: 462 | plugin_dir = 'cache' 463 | plugin_dir = os.path.join(os.getcwd(), plugin_dir) 464 | 465 | data_dir = path.join(plugin_dir, panel_name) 466 | if not path.exists(data_dir): 467 | os.makedirs(data_dir) 468 | return path.join(data_dir, panel_name) 469 | # ----------------- 470 | def set_layout_columns(count, coll_width=0.75): 471 | 472 | if count == 1: 473 | sublime.active_window().run_command("set_layout", {"cells": [[0, 0, 1, 1]], "cols": [0.0, 1.0], "rows": [0.0, 1.0]}) 474 | 475 | elif count == 2: 476 | sublime.active_window().run_command("set_layout", {"cells": [[0, 0, 1, 1], [1, 0, 2, 1]], "cols": [0.0, coll_width, 1.0], "rows": [0.0, 1.0]}) 477 | 478 | elif count == 3: 479 | sublime.active_window().run_command("set_layout", {"cells": [[0, 0, 1, 1], [1, 0, 2, 1], [2, 0, 3, 1]], "cols": [0.0, 0.33, 0.66, 1.0], "rows": [0.0, 1.0]}) 480 | 481 | elif count == 4: 482 | sublime.active_window().run_command("set_layout", {"cells": [[0, 0, 1, 1], [1, 0, 2, 1], [2, 0, 3, 1], [3, 0, 4, 1]], "cols": [0.0, 0.25, 0.5, 0.75, 1.0], "rows": [0.0, 1.0]}) 483 | # ----------------- 484 | def centre_line_of(view, region): 485 | (first_row,c) = view.rowcol(region.begin()) 486 | (last_row,c) = view.rowcol(region.end()) 487 | return int(first_row + (last_row - first_row)/2) 488 | # ----------------- 489 | def get_panel_view(): 490 | for v in sublime.active_window().views(): 491 | if v.file_name() == panel_file(): 492 | return v 493 | # ----------------- 494 | def refresh_panel_for(view): 495 | panel_view = get_panel_view() 496 | if panel_view: 497 | panel_view.run_command(plugin_name+'_generator') 498 | 499 | # =============================================================================== 500 | class event_listener(sublime_plugin.EventListener): 501 | panel_closed_group = -1 502 | pre_close_active = None 503 | can_close = False 504 | # ----------------- 505 | def on_load(self, view): 506 | if view.file_name() != panel_file(): 507 | refresh_panel_for(view) 508 | # ----------------- 509 | def on_pre_close(self, view): 510 | if view.file_name() == panel_file(): 511 | event_listener.panel_closed_group, x = sublime.active_window().get_view_index(view) 512 | if len(sublime.active_window().views_in_group(event_listener.panel_closed_group)) == 1: 513 | event_listener.can_close = True 514 | # ----------------- 515 | def on_close(self, view): 516 | 517 | def close_panel_group(): 518 | """Removes the panel group, and scales up the rest of the layout""" 519 | layout = window.get_layout() 520 | cols = layout['cols'] 521 | cells = layout['cells'] 522 | last_col = len(cols) - 1 523 | panel_width = cols[len(cols) - 2] 524 | 525 | for i, col in enumerate(cols): 526 | if col > 0: 527 | cols[i] = col/panel_width 528 | 529 | del cols[last_col] 530 | del cells[len(cells) - 1] 531 | window.run_command("set_layout", layout) 532 | 533 | def focus_source_code(): 534 | if event_listener.pre_close_active: 535 | window.focus_group(event_listener.pre_close_active[0]) 536 | window.focus_view(event_listener.pre_close_active[1]) 537 | 538 | enabled = settings().get('close_empty_group_on_closing_panel', True) 539 | 540 | if event_listener.can_close and enabled and view.file_name() == panel_file() and event_listener.panel_closed_group != -1: 541 | window = sublime.active_window() 542 | event_listener.can_close = False 543 | close_panel_group() 544 | sublime.set_timeout(focus_source_code, 100) 545 | 546 | event_listener.panel_closed_group = -1 547 | # ----------------- 548 | def on_post_save_async(self, view): 549 | refresh_panel_for(view) 550 | # ----------------- 551 | def on_activated_async(self, view): 552 | refresh_panel_for(view) 553 | # =============================================================================== 554 | class scroll_to_left(sublime_plugin.TextCommand): 555 | # ----------------- 556 | def panel_view(next_focus_view=None): 557 | 558 | def do(): 559 | get_panel_view().run_command('scroll_to_left') 560 | 561 | sublime.set_timeout(do, 100) 562 | # ----------------- 563 | def run(self, edit): 564 | region = self.view.visible_region() 565 | y = self.view.text_to_layout(region.begin())[1] 566 | self.view.set_viewport_position((0, y), False) 567 | 568 | # =============================================================================== 569 | class show_panel: 570 | # ----------------- 571 | def run(self, edit): 572 | 573 | def create_panel_group(): 574 | """Adds a column on the right, and scales down the rest of the layout""" 575 | layout = self.view.window().get_layout() 576 | cols = layout['cols'] 577 | cells = layout['cells'] 578 | last_col = len(cols) - 1 579 | last_row = len(layout['rows']) - 1 580 | width = 1 - settings().get("panel_width", 0.17) 581 | 582 | for i, col in enumerate(cols): 583 | if col > 0: 584 | cols[i] = col*width 585 | 586 | cols.append(1) 587 | newcell = [last_col, 0, last_col + 1, last_row] 588 | cells.append(newcell) 589 | window.run_command("set_layout", layout) 590 | groups = window.num_groups() 591 | return (groups + 1) 592 | 593 | window = self.view.window() 594 | groups = window.num_groups() 595 | current_group = window.active_group() 596 | current_view = self.view 597 | 598 | panel_view = get_panel_view() 599 | 600 | if not panel_view: 601 | panel_group = 1 602 | 603 | show_in_new_group = settings().get("show_in_new_group", True) 604 | 605 | if not show_in_new_group: 606 | if groups == 1: 607 | set_layout_columns(2) 608 | groups = window.num_groups() 609 | 610 | else: 611 | panel_group = create_panel_group() 612 | 613 | with open(panel_file(), "w") as file: 614 | file.write('') 615 | 616 | panel_view = window.open_file(panel_file()) 617 | panel_view.settings().set("word_wrap", False) 618 | window.set_view_index(panel_view, panel_group, 0) 619 | panel_view.sel().clear() 620 | 621 | panel_view.settings().set("gutter", False) 622 | 623 | def focus_source_code(): 624 | window.focus_group(current_group) 625 | window.focus_view(current_view) 626 | 627 | sublime.set_timeout_async(focus_source_code, 100) 628 | 629 | else: 630 | # close group only if panel is the only file in it 631 | panel_group = window.get_view_index(panel_view)[0] 632 | if len(window.views_in_group(panel_group)) == 1: 633 | event_listener.pre_close_active = [current_group, current_view] 634 | event_listener.can_close = True 635 | window.focus_view(panel_view) 636 | window.run_command("close_file") 637 | # =============================================================================== 638 | -------------------------------------------------------------------------------- /favorites.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | "close_empty_group_on_closing_panel": true, 3 | "favorite_files_integration_enabled": false, 4 | "show_in_new_group": true 5 | } 6 | -------------------------------------------------------------------------------- /images/commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oleg-shilo/sublime-favorites/517974836c538fa1d03e5aa5432aaddeb3c7f989/images/commands.png -------------------------------------------------------------------------------- /images/favorites.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oleg-shilo/sublime-favorites/517974836c538fa1d03e5aa5432aaddeb3c7f989/images/favorites.gif -------------------------------------------------------------------------------- /images/global_vs_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oleg-shilo/sublime-favorites/517974836c538fa1d03e5aa5432aaddeb3c7f989/images/global_vs_project.png -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "messages/install.txt", 3 | "1.0.1": "messages/1.0.1.txt", 4 | "1.0.2": "messages/1.0.2.txt", 5 | "1.0.3": "messages/1.0.3.txt", 6 | "1.0.4": "messages/1.0.4.txt" 7 | "1.0.5": "messages/1.0.5.txt" 8 | } 9 | -------------------------------------------------------------------------------- /messages/1.0.1.txt: -------------------------------------------------------------------------------- 1 | Features: 2 | 3 | 1. Added integration with `Favorite Files` plugin. 4 | The integration is limited to using `Favorite Files` data file, flattening it and allowing to open files on double-click on the item in the Favorites panel. 5 | 6 | In order to enable `Favorite Files` integration set `favorite_files_integration_enabled` setting to true: 7 | ```js 8 | { 9 | "favorite_files_integration_enabled": true 10 | } 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /messages/1.0.2.txt: -------------------------------------------------------------------------------- 1 | Features: 2 | 3 | 1. Fixed Issue #1: Crash; nothing showing up in Favorites panel 4 | -------------------------------------------------------------------------------- /messages/1.0.3.txt: -------------------------------------------------------------------------------- 1 | Features/Changes: 2 | 3 | 1. Added commands for all major operations 4 | 2. Issues #2: Allow favorites.txt file to be created per-project, at project root 5 | 3. Issues #3: Allow favorites.yml as alternative to favorites.txt 6 | By default the items in the favorites panel appears with their file name listed. However if a custom name is more beneficial a file alias can be assigned to the item in the data file via "Edit favorites" command. 7 | 8 | The format is simple: |: 9 | ``` 10 | shelloverlay|Q:\Extras\Utils\TortoiseIconOverlays.cs 11 | ``` 12 | 4. Issues #4: Allow opening all favorites with a single command 13 | -------------------------------------------------------------------------------- /messages/1.0.4.txt: -------------------------------------------------------------------------------- 1 | Features/Changes: 2 | 3 | 1. Fixed problem with opening favorite items in absence of a loaded project/folder. -------------------------------------------------------------------------------- /messages/1.0.5.txt: -------------------------------------------------------------------------------- 1 | Features/Changes: 2 | 3 | 1. Feature request #6: Added 'remove all' command -------------------------------------------------------------------------------- /messages/1.0.7.txt: -------------------------------------------------------------------------------- 1 | Features/Changes: 2 | 3 | 1. Removed default key binding as it conflicts with default `alt+f` OS handling. 4 | Users should configure custom binding instead: 5 | _Open keymap menu_ 6 | Preferences > Key Bindings > Default (...).sublime-keymap - User 7 | _Add mapping:_ 8 | ``` 9 | { "keys": ["alt+f", "alt+f"], "command": "show_favorites" } 10 | ``` -------------------------------------------------------------------------------- /messages/install.txt: -------------------------------------------------------------------------------- 1 | Welcome to Favorites! 2 | 3 | For more information see the README.md at https://github.com/oleg-shilo/sublime-favorites 4 | --------------------------------------------------------------------------------------- 5 | A plugin for displaying Favorites (list of frequently used documents) in the Sublime Text 3 editor. 6 | 7 | While plugin functionality overlaps with some other similar ST3 plugins, it has vary strong distinction - it offers visual management of the Favorites' items. 8 | 9 | Usage 10 | The plugin uses a dedicated view group Favorites (on right side) to mimic a "side bar" with the content (code tree). 11 | 12 | * Toggle Visibility 13 | Show/Hide favorites panel either with Command Palette or by the configured shortcut (e.g. [alt+f, alt+f]). 14 | 15 | * Add active document to favorites 16 | Double-click the Add command at the top of the panel. 17 | 18 | * Remove the item (document) from the favorites. 19 | Hover over the document item in the panel and then click Remove from... on the tooltip. 20 | 21 | * Open the item (document) 22 | Double-click the document item in the panel. 23 | 24 | *Edit Favorites data file directly 25 | Double-click the Edit command at the top of the panel. 26 | --------------------------------------------------------------------------------