├── .gitignore ├── htmlContent ├── bookmarks-list.html └── bookmarks-panel.html ├── .brackets.json ├── package.json ├── images └── bookmark.svg ├── styles └── styles.css ├── nls ├── ru │ └── strings.js ├── root │ └── strings.js ├── pl │ └── strings.js ├── es │ └── strings.js ├── fr │ └── strings.js ├── it │ └── strings.js ├── de │ └── strings.js └── strings.js ├── strings.js ├── .jshintrc ├── README.md ├── view └── BookmarksView.js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /htmlContent/bookmarks-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{#bookmarks}} 4 | 5 | 6 | 7 | {{/bookmarks}} 8 | 9 |
{{fullPath}}:{{lineNo}}
10 | -------------------------------------------------------------------------------- /htmlContent/bookmarks-panel.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{Strings.BOOKMARKS_PANEL_TITLE}}
4 | × 5 |
6 |
7 |
8 | -------------------------------------------------------------------------------- /.brackets.json: -------------------------------------------------------------------------------- 1 | { 2 | "jslint.options": { 3 | "vars": true, 4 | "plusplus": true, 5 | "devel": true, 6 | "nomen": true, 7 | "maxerr": 50, 8 | "es5": true 9 | }, 10 | "defaultExtension": "js", 11 | "language": { 12 | "javascript": { 13 | "linting.prefer": ["JSLint", "JSHint"], 14 | "linting.usePreferredOnly": true 15 | } 16 | }, 17 | "spaceUnits": 4, 18 | "useTabChar": false 19 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jeffbooher.bookmarks", 3 | "title": "Brackets Editor Bookmarks", 4 | "description": "Bookmark lines in the editor as you work so you can quickly jump back to them later. Open the \"Bookmarks\" Panel to see all of the bookmarks and open them in the editor.", 5 | "homepage": "https://github.com/JeffryBooher/brackets-bookmarks-extension", 6 | "version": "0.10.6", 7 | "author": "Jeffry Booher|jsbooher@adobe.com (http://www.facebook.com/JeffryBooher)|@JeffryBooher", 8 | "license": "MIT", 9 | "i18n": [ 10 | "en", 11 | "de", 12 | "es", 13 | "fr", 14 | "it", 15 | "pl", 16 | "ru" 17 | ], 18 | "engines": { 19 | "brackets": ">=0.24.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /images/bookmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/styles.css: -------------------------------------------------------------------------------- 1 | .bookmark .CodeMirror-gutter-elt { 2 | background-color: lightskyblue; 3 | } 4 | 5 | .bookmark .CodeMirror-linenumber { 6 | background-image: url('../images/bookmark.svg'); 7 | background-size: 16px; 8 | background-repeat: no-repeat; 9 | color: white !important; 10 | } 11 | 12 | .active-pane .bookmark .CodeMirror-gutter-elt { 13 | background-color: dodgerblue; 14 | display: table; 15 | } 16 | 17 | .active-pane .bookmark.live-preview-sync-error .CodeMirror-linenumber { 18 | background-color: mediumorchid !important; 19 | } 20 | 21 | .bookmark-notify { 22 | background-color: dodgerblue; 23 | } 24 | 25 | .bookmark-notify .CodeMirror-activeline-background { 26 | background-color: darkslateblue!important; 27 | } 28 | 29 | .bookmark.CodeMirror-activeline .CodeMirror-gutter-elt { 30 | background-color: rebeccapurple !important; 31 | } 32 | 33 | .active-pane .bookmark.CodeMirror-activeline .CodeMirror-gutter-elt { 34 | background-color: darkslateblue!important; 35 | } 36 | 37 | .bookmark.CodeMirror-activeline .CodeMirror-linenumber { 38 | background-image: url('../images/bookmark.svg') !important; 39 | background-size: 16px !important; 40 | background-repeat: no-repeat !important; 41 | color: white !important; 42 | } 43 | -------------------------------------------------------------------------------- /nls/ru/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | // Russian Translation 25 | 26 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 27 | /*global define */ 28 | 29 | define({ 30 | "TOGGLE_BOOKMARK" : "Вкл./выкл. закладку", 31 | "GOTO_NEXT_BOOKMARK" : "Следующая закладка", 32 | "GOTO_PREV_BOOKMARK" : "Предыдущая закладка", 33 | "TOGGLE_BOOKMARKS_PANEL" : "Показать панель закладок", 34 | "BOOKMARKS_PANEL_TITLE" : "Закладки" 35 | }); 36 | -------------------------------------------------------------------------------- /nls/root/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | // English - root strings 25 | 26 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 27 | /*global define */ 28 | 29 | define({ 30 | "TOGGLE_BOOKMARK" : "Toggle Bookmark", 31 | "GOTO_NEXT_BOOKMARK" : "Next Bookmark", 32 | "GOTO_PREV_BOOKMARK" : "Previous Bookmark", 33 | "TOGGLE_BOOKMARKS_PANEL" : "Show Bookmarks Panel", 34 | "BOOKMARKS_PANEL_TITLE" : "Bookmarks" 35 | }); 36 | -------------------------------------------------------------------------------- /nls/pl/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | // Polish Translation 25 | 26 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 27 | /*global define */ 28 | 29 | define({ 30 | "TOGGLE_BOOKMARK" : "Włącz/Wyłącz Zakładki", 31 | "GOTO_NEXT_BOOKMARK" : "Następna Zakładka", 32 | "GOTO_PREV_BOOKMARK" : "Poprzednia Zakładka", 33 | "TOGGLE_BOOKMARKS_PANEL" : "Pokaż Panel Zakładek", 34 | "BOOKMARKS_PANEL_TITLE" : "Zakładki" 35 | }); 36 | -------------------------------------------------------------------------------- /nls/es/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | // English - root strings 25 | 26 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 27 | /*global define */ 28 | 29 | define({ 30 | "TOGGLE_BOOKMARK" : "Des/Activar Marcador", 31 | "GOTO_NEXT_BOOKMARK" : "Marcador Siguiente", 32 | "GOTO_PREV_BOOKMARK" : "Marcador Anterior", 33 | "TOGGLE_BOOKMARKS_PANEL" : "Mostrar Panel de Marcadores", 34 | "BOOKMARKS_PANEL_TITLE" : "Marcadores" 35 | }); 36 | -------------------------------------------------------------------------------- /nls/fr/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | // French Translation 25 | 26 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 27 | /*global define */ 28 | 29 | define({ 30 | "TOGGLE_BOOKMARK" : "Créer/supprimer le marque-page", 31 | "GOTO_NEXT_BOOKMARK" : "Marque-page suivant", 32 | "GOTO_PREV_BOOKMARK" : "Marque-page précédent", 33 | "TOGGLE_BOOKMARKS_PANEL" : "Afficher panneau des marque-pages", 34 | "BOOKMARKS_PANEL_TITLE" : "Marque-pages" 35 | }); 36 | -------------------------------------------------------------------------------- /nls/it/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | // Italian - root strings 25 | 26 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 27 | /*global define */ 28 | 29 | define({ 30 | "TOGGLE_BOOKMARK" : "Toggle Bookmark", 31 | "GOTO_NEXT_BOOKMARK" : "Segnalibro successivo", 32 | "GOTO_PREV_BOOKMARK" : "Segnalibro precedente", 33 | "TOGGLE_BOOKMARKS_PANEL" : "Mostra pannello segnalibri", 34 | "BOOKMARKS_PANEL_TITLE" : "Segnalibri" 35 | }); 36 | -------------------------------------------------------------------------------- /nls/de/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | // German strings 25 | 26 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 27 | /*global define */ 28 | 29 | define({ 30 | "TOGGLE_BOOKMARK" : "Lesezeichen setzen/entfernen", 31 | "GOTO_NEXT_BOOKMARK" : "Gehe zum nächsten Lesezeichen", 32 | "GOTO_PREV_BOOKMARK" : "Gehe zum vorherigen Lesezeichen", 33 | "TOGGLE_BOOKMARKS_PANEL" : "Lesezeichen anzeigen...", 34 | "BOOKMARKS_PANEL_TITLE" : "Lesezeichen" 35 | }); 36 | -------------------------------------------------------------------------------- /strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 25 | /*global define */ 26 | 27 | /** 28 | * This file provides the interface to user visible strings in Brackets. Code that needs 29 | * to display strings should should load this module by calling var Strings = require("strings"). 30 | * The i18n plugin will dynamically load the strings for the right locale and populate 31 | * the exports variable. See src\nls\strings.js for the master file of English strings. 32 | */ 33 | define(function (require, exports, module) { 34 | "use strict"; 35 | 36 | module.exports = require("i18n!nls/strings"); 37 | 38 | }); -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise" : true, 3 | "curly" : true, 4 | "eqeqeq" : true, 5 | "forin" : true, 6 | "immed" : true, 7 | "latedef" : true, 8 | "newcap" : true, 9 | "noarg" : true, 10 | "noempty" : true, 11 | "nonew" : true, 12 | "plusplus" : false, 13 | "regexp" : true, 14 | "undef" : true, 15 | "strict" : true, 16 | "unused" : "vars", 17 | 18 | "asi" : false, 19 | "boss" : false, 20 | "debug" : false, 21 | "eqnull" : false, 22 | "es5" : false, 23 | "esnext" : false, 24 | "evil" : false, 25 | "expr" : false, 26 | "funcscope" : false, 27 | "globalstrict" : false, 28 | "iterator" : false, 29 | "lastsemic" : false, 30 | "laxbreak" : false, 31 | "laxcomma" : false, 32 | "loopfunc" : false, 33 | "multistr" : false, 34 | "onecase" : false, 35 | "proto" : false, 36 | "regexdash" : false, 37 | "scripturl" : false, 38 | "shadow" : false, 39 | "sub" : false, 40 | "supernew" : false, 41 | "validthis" : false, 42 | 43 | "browser" : false, 44 | "couch" : false, 45 | "devel" : true, 46 | "dojo" : false, 47 | "jquery" : false, 48 | "mootools" : false, 49 | "node" : false, 50 | "nonstandard" : false, 51 | "prototypejs" : false, 52 | "rhino" : false, 53 | "wsh" : false, 54 | 55 | "maxerr" : 100, 56 | "indent" : 4, 57 | "globals" : { 58 | "window": false, 59 | "document": false, 60 | "setTimeout": false, 61 | "require": false, 62 | "define": false, 63 | "brackets": false, 64 | "$": false, 65 | "PathUtils": false, 66 | "window": false, 67 | "navigator": false, 68 | "Mustache": false 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /nls/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 25 | /*global define */ 26 | 27 | define(function (require, exports, module) { 28 | 29 | 'use strict'; 30 | 31 | // Code that needs to display user strings should call require("strings") to load 32 | // strings.js. This file will dynamically load strings.js for the specified by bracketes.locale. 33 | // 34 | // Translations for other locales should be placed in nls/>/strings.js 35 | // Localization is provided via the i18n plugin. 36 | // All other bundles for languages need to add a prefix to the exports below so i18n can find them. 37 | // TODO: dynamically populate the local prefix list below? 38 | module.exports = { 39 | root: true, 40 | "de": true, 41 | "es": true, 42 | "fr": true, 43 | "it": true, 44 | "pl": true, 45 | "ru": true 46 | }; 47 | }); 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Brackets Bookmarks Extension 2 | ============================ 3 | 4 | This Extension provides functionality to bookmark lines in [Brackets](https://github.com/adobe/brackets). 5 | 6 | `Navigate > Toggle Bookmark` or press ⇧⌘K (`Ctrl+Shift+K` on Windows) 7 | 8 | And recall those bookmarks later 9 | 10 | `Navigate > Next Bookmark` or press ⌘P (`Ctrl+P` on Windows) 11 | 12 | `Navigate > Previous Bookmark` or press ⇧⌘P (`Ctrl+Shift+P` on Windows) 13 | 14 | Bookmarks are serialized and remembered globally. Add bookmarks to a file, close the file, reopen the file. Brackets will restore the bookmarks. Bookmarks are represented with a color and a bookmark icon. 15 | 16 | Bookmarked lines with Live Editing Syntax Errors will show using a different color 17 | 18 | `View > Show Bookmarks Panel` To see all bookmarks of open files (open, temporary views and views of files in the working set which may not have been open yet) 19 | 20 | The default mode for the Bookmarks panel is to show bookmarks of only "open" files. This can be confusing when bookmarking files that Brackets has created a temporary view for then switching to another view. The result would cause the bookmark to disappear from the bookmarks view with the only way to get back to it is by reopening the file. 21 | 22 | Add the following to your Brackets preferences file to show all bookmarks within a "project" 23 | 24 | "bracketsEditorBookmarks.viewOptions": { 25 | "show": "project" 26 | } 27 | 28 | 29 | Add the following to your Brackets preferences file to show all bookmarks 30 | 31 | "bracketsEditorBookmarks.viewOptions": { 32 | "show": "all" 33 | } 34 | 35 | the default for the "show" preference is "opened". 36 | 37 | **ProTip**: Setting the show preference "all" will enable you to quickly jump between files in other projects. 38 | 39 | ##TODO 40 | 1. Localize. I've set this extension up for localization so please contribute pull requests to translate this extension 41 | 1. Sync bookmarks if files are changed externally... Use a hash of the line maybe? 42 | 1. Better Bookmark Affordance (@larz0) especially when bookmarking lines in files with more than 999 lines since the bookmark UI encroaches on the line number 43 | 1. Bookmark UI when line numbers are turned off 44 | 1. Bookmark filtering (show bookmarks with xxx in the file's name, annotation, etc..) 45 | 1. Bookmark groups (allow bookmarks to belong to a task like "refactoring functionXXX") 46 | 1. Bookmark annotation (why was the bookmark added -- a quick glance at the context of the bookmark) 47 | -------------------------------------------------------------------------------- /view/BookmarksView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*eslint-rules no-underscore-dangle: false*/ 25 | 26 | /*global define, brackets, window, $, Mustache, navigator */ 27 | 28 | /* 29 | * Panel showing search results for a Find/Replace in Files operation. 30 | */ 31 | define(function (require, exports, module) { 32 | "use strict"; 33 | 34 | var CommandManager = brackets.getModule("command/CommandManager"), 35 | Commands = brackets.getModule("command/Commands"), 36 | DocumentManager = brackets.getModule("document/DocumentManager"), 37 | EditorManager = brackets.getModule("editor/EditorManager"), 38 | ProjectManager = brackets.getModule("project/ProjectManager"), 39 | FileViewController = brackets.getModule("project/FileViewController"), 40 | FileUtils = brackets.getModule("file/FileUtils"), 41 | FindUtils = brackets.getModule("search/FindUtils"), 42 | WorkspaceManager = brackets.getModule("view/WorkspaceManager"), 43 | MainViewManger = brackets.getModule("view/MainViewManager"), 44 | _ = brackets.getModule("thirdparty/lodash"), 45 | 46 | StringUtils = brackets.getModule("utils/StringUtils"), 47 | Strings = require("strings"), 48 | 49 | bookmarksPanelTemplate = require("text!htmlContent/bookmarks-panel.html"), 50 | bookmarksListTemplate = require("text!htmlContent/bookmarks-list.html"); 51 | 52 | 53 | /** 54 | * @const 55 | * Debounce time for document changes updating the search results view. 56 | * @type {number} 57 | */ 58 | var UPDATE_TIMEOUT = 400; 59 | 60 | /** 61 | * @const 62 | * MainViewManager events 63 | * @type {string} 64 | */ 65 | var MVM_EVENTS = "workingSetAdd workingSetAddList workingSetMove workingSetRemove workingSetRemoveList workingSetUpdate currentFileChange activePaneChange"; 66 | /** 67 | * @constructor 68 | * Creates a bookmarks panel 69 | * Dispatches the following events: 70 | * close - when the panel is closed. 71 | * 72 | * @typedef {Object.>} BookmarksModel 73 | * @param {BookmarksModel} model - bookmarks model 74 | * @param {function=} beforeRender - function to call before rendering the view 75 | */ 76 | function BookmarksView(model, beforeRender) { 77 | var panelHtml = Mustache.render(bookmarksPanelTemplate, { 78 | Strings: Strings 79 | }); 80 | 81 | this._panel = WorkspaceManager.createBottomPanel("bookmarks", $(panelHtml), 100); 82 | this._$panel = this._panel.$panel; 83 | this._$table = this._$panel.find(".table-container"); 84 | this._model = model; 85 | this._beforeRender = beforeRender; 86 | } 87 | 88 | /** @type {BookmarksModel} bookmarks model */ 89 | BookmarksView.prototype._model = null; 90 | 91 | /** @type {Panel} Bottom panel holding the bookmarks */ 92 | BookmarksView.prototype._panel = null; 93 | 94 | /** @type {$.Element} The table that holds the results */ 95 | BookmarksView.prototype._$table = null; 96 | 97 | /** @type {number} The ID we use for timeouts when handling model changes. */ 98 | BookmarksView.prototype._timeoutID = null; 99 | 100 | /** @type {function=} function that is called before refreshing the view */ 101 | BookmarksView.prototype._beforeRender = null; 102 | 103 | /** 104 | * @private 105 | * Handles when model changes. Updates the view, buffering changes if necessary so as not to churn too much. 106 | */ 107 | BookmarksView.prototype._handleModelChange = function () { 108 | var self = this; 109 | if (self._ignoreModelChangeEvents) { 110 | return; 111 | } 112 | if (this._timeoutID) { 113 | window.clearTimeout(this._timeoutID); 114 | } 115 | this._timeoutID = window.setTimeout(function () { 116 | // _updateResults causes the model to be recomputed 117 | // which triggers another model change event which 118 | // we need to ignore or we endup in a race condition 119 | // which may lead to data loss 120 | self._ignoreModelChangeEvents = true; 121 | self._updateResults(); 122 | self._timeoutID = null; 123 | delete self._ignoreModelChangeEvents; 124 | }, UPDATE_TIMEOUT); 125 | }; 126 | 127 | /** 128 | * @private 129 | * Adds the listeners for close and clicking on a bookmark in the list 130 | */ 131 | BookmarksView.prototype._addPanelListeners = function () { 132 | var self = this; 133 | this._$panel 134 | .off(".bookmarks") // Remove the old events 135 | .on("click.bookmarks", ".close", function () { 136 | self.close(); 137 | }) 138 | // Add the click event listener directly on the table parent 139 | .on("click.bookmarks .table-container", function (e) { 140 | var $row = $(e.target).closest("tr"); 141 | 142 | if ($row.length) { 143 | if (self._$selectedRow) { 144 | self._$selectedRow.removeClass("selected"); 145 | } 146 | $row.addClass("selected"); 147 | self._$selectedRow = $row; 148 | 149 | var fullPathAndLineNo = $row.find(".bookmark-result").text(); 150 | 151 | CommandManager.execute(Commands.FILE_OPEN, {fullPath: fullPathAndLineNo}); 152 | } 153 | }); 154 | MainViewManger.on(MVM_EVENTS, this._updateResults.bind(this)); 155 | }; 156 | 157 | /** 158 | * @private 159 | * @param {!String} fullpath - path of the file to show 160 | */ 161 | BookmarksView.prototype._shouldShow = function (fullpath) { 162 | if (!this._options || !this._options.show || this._options.show === "opened") { 163 | return Boolean(MainViewManger._getPaneIdForPath(fullpath)); 164 | } else if (this._options.show === "all") { 165 | return true; 166 | 167 | } else if (this._options.show === "project" && ProjectManager.getProjectRoot()) { 168 | // show open files and any file bookmarked in the current project 169 | return (Boolean(MainViewManger._getPaneIdForPath(fullpath)) || 170 | fullpath.toLowerCase().indexOf(ProjectManager.getProjectRoot().fullPath.toLowerCase()) === 0); 171 | } 172 | 173 | // unknown option 174 | return false; 175 | }; 176 | 177 | /** 178 | * @private 179 | * Shows the current set of results. 180 | */ 181 | BookmarksView.prototype._render = function () { 182 | var self = this, 183 | bookmarks = []; 184 | 185 | if (this._beforeRender) { 186 | this._beforeRender(); 187 | } 188 | 189 | // Iterates throuh the files to display the results sorted by filenamess. The loop ends as soon as 190 | // we filled the results for one page 191 | Object.keys(this._model) 192 | .filter(function (fullPath) { 193 | return self._shouldShow(fullPath); 194 | }) 195 | .sort(function (a, b) { 196 | return a > b; 197 | }) 198 | .forEach(function (fullPath) { 199 | self._model[fullPath].forEach(function (lineNo) { 200 | bookmarks.push({ 201 | fullPath: fullPath, 202 | lineNo: lineNo + 1 203 | }); 204 | }); 205 | }); 206 | 207 | // Insert the search results 208 | this._$table 209 | .empty() 210 | .append(Mustache.render(bookmarksListTemplate, { 211 | bookmarks: bookmarks, 212 | Strings: Strings 213 | })); 214 | 215 | if (this._$selectedRow) { 216 | this._$selectedRow.removeClass("selected"); 217 | this._$selectedRow = null; 218 | } 219 | 220 | this._panel.show(); 221 | this._$table.scrollTop(0); // Otherwise scroll pos from previous contents is remembered 222 | }; 223 | 224 | /** 225 | * Updates the results view after a model change, preserving scroll position and selection. 226 | */ 227 | BookmarksView.prototype._updateResults = function () { 228 | // In general this shouldn't get called if the panel is closed, but in case some 229 | // asynchronous process kicks this (e.g. a debounced model change), we double-check. 230 | if (this._panel.isVisible()) { 231 | var scrollTop = this._$table.scrollTop(), 232 | index = this._$selectedRow ? this._$selectedRow.index() : null; 233 | this._render(); 234 | this._$table.scrollTop(scrollTop); 235 | if (index) { 236 | this._$selectedRow = this._$table.find("tr:eq(" + index + ")"); 237 | this._$selectedRow.addClass("selected"); 238 | } 239 | } 240 | }; 241 | 242 | 243 | 244 | 245 | /** 246 | * Opens the results panel and displays the current set of results from the model. 247 | */ 248 | BookmarksView.prototype.open = function (options) { 249 | this._options = options; 250 | this._render(); 251 | this._addPanelListeners(); 252 | $(this._model).on("change.BookmarksView", this._handleModelChange.bind(this)); 253 | }; 254 | 255 | /** 256 | * Hides the Search Results Panel and unregisters listeners. 257 | */ 258 | BookmarksView.prototype.close = function () { 259 | if (this._panel && this._panel.isVisible()) { 260 | this._$table.empty(); 261 | this._panel.hide(); 262 | this._panel.$panel.off(".bookmarks"); 263 | $(this._model).off("change.BookmarksView"); 264 | $(this).triggerHandler("close"); 265 | } 266 | }; 267 | 268 | /** 269 | * Hides the Search Results Panel and unregisters listeners. 270 | */ 271 | BookmarksView.prototype.isOpen = function () { 272 | return (this._panel && this._panel.isVisible()); 273 | }; 274 | 275 | // Public API 276 | exports.BookmarksView = BookmarksView; 277 | }); 278 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Jeffry Booher. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */ 25 | /*global define, brackets, $ */ 26 | 27 | define(function (require, exports, module) { 28 | "use strict"; 29 | 30 | // Brackets modules 31 | var PreferencesManager = brackets.getModule("preferences/PreferencesManager"), 32 | CommandManager = brackets.getModule("command/CommandManager"), 33 | ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), 34 | Menus = brackets.getModule("command/Menus"), 35 | DocumentManager = brackets.getModule("document/DocumentManager"), 36 | EditorManager = brackets.getModule("editor/EditorManager"), 37 | _ = brackets.getModule("thirdparty/lodash"); 38 | 39 | // my modules 40 | var BookmarksView = require("view/BookmarksView").BookmarksView, 41 | ExtensionStrings = require("strings"); 42 | 43 | 44 | /** @const {string} Extension Command ID */ 45 | var MY_MODULENAME = "bracketsEditorBookmarks"; 46 | var CMD_TOGGLE_BOOKMARK = "bracketsEditorBookmarks.toggleBookmark", 47 | CMD_GOTO_NEXT_BOOKMARK = "bracketsEditorBookmarks.gotoNextBookmark", 48 | CMD_GOTO_PREV_BOOKMARK = "bracketsEditorBookmarks.gotoPrevBookmark", 49 | CMD_TOGGLE_BOOKKMARK_VIEW = "bracketsEditorBookmarks.toggleBookmarksPanel"; 50 | 51 | /* Our extension's preferences */ 52 | var prefs = PreferencesManager.getExtensionPrefs(MY_MODULENAME); 53 | 54 | // Bookmarks Data Model 55 | var _bookmarks = {}; 56 | 57 | // Bookmarks Panel 58 | var _bookmarksPanel = null; 59 | 60 | /** 61 | * Saves bookmarks to the data model for the specified editor instance 62 | * @param {Editor=} editor - brackets editor instance. current editor if null 63 | * @return {?Array.} array of cached bookmarked line numbers 64 | */ 65 | function saveBookmarks(editor) { 66 | if (!editor) { 67 | editor = EditorManager.getCurrentFullEditor(); 68 | } 69 | if (editor) { 70 | var i, 71 | fullPath = editor.document.file.fullPath, 72 | cm = editor._codeMirror, 73 | lineCount = cm.doc.lineCount(), 74 | bookmarkedLines = []; 75 | 76 | for (i = 0; i < lineCount; i++) { 77 | var lineInfo = cm.lineInfo(i); 78 | 79 | if (lineInfo.wrapClass && lineInfo.wrapClass.indexOf("bookmark") >= 0) { 80 | bookmarkedLines.push(i); 81 | } 82 | } 83 | 84 | // we need to sort so that go to next bookmark works 85 | bookmarkedLines.sort(function (a, b) { 86 | return a > b; 87 | }); 88 | 89 | _bookmarks[fullPath] = bookmarkedLines; 90 | prefs.set("bookmarks", _bookmarks); 91 | 92 | $(_bookmarks).triggerHandler("change"); 93 | 94 | // return the bookmarks for the editor 95 | return bookmarkedLines; 96 | } 97 | return null; 98 | } 99 | 100 | /** 101 | * Updates bookmarks for the current editor if necessary 102 | * @param {Editor=} editor - brackets editor instance. current editor if null 103 | * @return {Boolean} true if there are bookmarks for the current editor, false if not 104 | */ 105 | function updateBookmarksForCurrentEditor() { 106 | var result = false, 107 | editor = EditorManager.getCurrentFullEditor(); 108 | if (editor) { 109 | var fullPath = editor.document.file.fullPath, 110 | bm = _bookmarks[fullPath]; 111 | 112 | // if there was already data then we 113 | // don't need to rebuild it 114 | result = (bm && bm.length); 115 | 116 | if (!result) { 117 | // there was no deta for this file so 118 | // rebuild the model just for this file 119 | // from what is in the editor currently 120 | result = Boolean(saveBookmarks(editor)); 121 | } 122 | } 123 | 124 | return result; 125 | } 126 | 127 | /** 128 | * Resets the bookmarks for the file opened in the specified editor 129 | * NOTE: When the bookmarks for the current editor are needed 130 | * (for traversal or to update the bookmarks panel), 131 | * updateBookmarksForCurrentEditor is called which updates 132 | * incrementally the bookmarks for the current file 133 | * @param {!Editor} editor - brackets editor instance 134 | */ 135 | function resetBookmarks(editor) { 136 | if (editor) { 137 | delete _bookmarks[editor.document.file.fullPath]; 138 | $(_bookmarks).triggerHandler("change"); 139 | } 140 | } 141 | 142 | /** 143 | * Loads the cached bookmarks into the specified editor instance 144 | * @param {Editor=} editor - brackets editor instance. current editor if null 145 | */ 146 | function loadBookmarks(editor) { 147 | if (!editor) { 148 | editor = EditorManager.getCurrentFullEditor(); 149 | } 150 | if (editor) { 151 | var cm = editor._codeMirror, 152 | bm = _bookmarks[editor.document.file.fullPath]; 153 | 154 | if (bm) { 155 | bm.forEach(function (lineNo) { 156 | if (lineNo < cm.doc.lineCount()) { 157 | cm.addLineClass(lineNo, "wrap", "bookmark"); 158 | } 159 | }); 160 | } 161 | } 162 | } 163 | 164 | 165 | /** 166 | * Removes all bookmarks from the editor 167 | * @param {!Editor} editor - brackets editor instance. 168 | */ 169 | function clearBookmarks(editor) { 170 | var i, 171 | cm = editor._codeMirror, 172 | lineCount = cm.doc.lineCount(); 173 | 174 | for (i = 0; i < lineCount; i++) { 175 | cm.removeLineClass(i, "wrap", "bookmark"); 176 | } 177 | } 178 | 179 | /** 180 | * Resets all bookmark model data so it can be re-read from prefs 181 | */ 182 | function resetModel() { 183 | Object.keys(_bookmarks).forEach(function (prop) { 184 | delete _bookmarks[prop]; 185 | }); 186 | } 187 | 188 | /** 189 | * Loads bookmark model from prefs 190 | */ 191 | function loadModel() { 192 | resetModel(); 193 | _.assign(_bookmarks, prefs.get("bookmarks")); 194 | } 195 | 196 | /** 197 | * Reloads the bookmark model, clearing the current bookmarks 198 | * @param {!Editor} editor - brackets editor instance. 199 | */ 200 | function reloadModel() { 201 | DocumentManager.getAllOpenDocuments().forEach(function (doc) { 202 | if (doc._masterEditor) { 203 | clearBookmarks(doc._masterEditor); 204 | } 205 | }); 206 | 207 | loadModel(); 208 | 209 | DocumentManager.getAllOpenDocuments().forEach(function (doc) { 210 | if (doc._masterEditor) { 211 | loadBookmarks(doc._masterEditor); 212 | } 213 | }); 214 | 215 | // update the panel 216 | $(_bookmarks).triggerHandler("change"); 217 | } 218 | 219 | /** 220 | * Moves the cursor position of the current editor to the next bookmark 221 | * @param {!Editor} editor - brackets editor instance 222 | */ 223 | function gotoNextBookmark(forward) { 224 | if (updateBookmarksForCurrentEditor()) { 225 | var editor = EditorManager.getCurrentFullEditor(), 226 | cursor = editor.getCursorPos(), 227 | bm = _bookmarks[editor.document.file.fullPath]; 228 | 229 | var doJump = function (lineNo) { 230 | editor.setCursorPos(lineNo, 0); 231 | 232 | var cm = editor._codeMirror; 233 | cm.addLineClass(lineNo, "wrap", "bookmark-notify"); 234 | setTimeout(function () { 235 | cm.removeLineClass(lineNo, "wrap", "bookmark-notify"); 236 | }, 100); 237 | }; 238 | 239 | // find next bookmark 240 | var index; 241 | for (index = (forward ? 0 : bm.length - 1); forward ? (index < bm.length) : (index >= 0); forward ? (index++) : (index--)) { 242 | if (forward) { 243 | if (bm[index] > cursor.line) { 244 | doJump(bm[index]); 245 | return; 246 | } 247 | if (index === bm.length - 1) { 248 | // wrap around just pick the first one in the list 249 | if (bm[0] !== cursor.line) { 250 | doJump(bm[0]); 251 | } 252 | return; 253 | } 254 | } else { 255 | if (bm[index] < cursor.line) { 256 | doJump(bm[index]); 257 | return; 258 | } 259 | if (index === 0) { 260 | // wrap around just pick the last one in the list 261 | if (bm[bm.length - 1] !== cursor.line) { 262 | doJump(bm[bm.length - 1]); 263 | } 264 | return; 265 | } 266 | } 267 | } 268 | } 269 | } 270 | 271 | /** 272 | * Toogles the bookmarked state of the current line of the current editor 273 | */ 274 | function toggleBookmark() { 275 | var editor = EditorManager.getCurrentFullEditor(); 276 | if (editor) { 277 | var cursor = editor.getCursorPos(), 278 | lineNo = cursor.line, 279 | cm = editor._codeMirror, 280 | lineInfo = cm.lineInfo(cursor.line); 281 | 282 | if (!lineInfo.wrapClass || lineInfo.wrapClass.indexOf("bookmark") === -1) { 283 | cm.addLineClass(lineNo, "wrap", "bookmark"); 284 | } else { 285 | cm.removeLineClass(lineNo, "wrap", "bookmark"); 286 | } 287 | resetBookmarks(editor); 288 | } 289 | } 290 | 291 | /** 292 | * Creates the bookmarks panel if it's truly needed 293 | * @param {Boolean} panelRequired - true if panel is required. False if not 294 | */ 295 | function createBookmarksPanelIfNecessary(panelRequired) { 296 | if (!_bookmarksPanel && panelRequired) { 297 | _bookmarksPanel = new BookmarksView(_bookmarks, updateBookmarksForCurrentEditor); 298 | 299 | $(_bookmarksPanel).on("close", function () { 300 | CommandManager.get(CMD_TOGGLE_BOOKKMARK_VIEW).setChecked(_bookmarksPanel.isOpen()); 301 | }); 302 | } 303 | } 304 | 305 | /** 306 | * Shows the bookmarks panel 307 | * @param {Boolean} show - true to show the panel, false to hide it 308 | * @param {{show:string=}=} options - undefined, {show: undefined|"opened"|"project"|"all"}, defaults to "opened" 309 | */ 310 | function showBookmarksPanel(show, options) { 311 | // we only need to create it if we're showing it 312 | createBookmarksPanelIfNecessary(show); 313 | if (!_bookmarksPanel) { 314 | // nothing to do, panel wasn't created 315 | return; 316 | } 317 | 318 | // show/hide the panel 319 | if (show) { 320 | _bookmarksPanel.open(options); 321 | } else { 322 | _bookmarksPanel.close(); 323 | } 324 | 325 | // update the command state 326 | CommandManager.get(CMD_TOGGLE_BOOKKMARK_VIEW).setChecked(_bookmarksPanel.isOpen()); 327 | } 328 | 329 | /** 330 | * Creates and/or Shows or Hides the bookmarks panel 331 | */ 332 | function toggleBookmarksPanel() { 333 | // always create it 334 | createBookmarksPanelIfNecessary(true); 335 | showBookmarksPanel(!_bookmarksPanel.isOpen(), prefs.get("viewOptions")); 336 | // Since this is the only user-facing command then 337 | // we can safely update the prefs here... 338 | // Updating it in showBookmarksPanel could result in 339 | // a race condition so we would need a way to 340 | // indicate whether we were initializing from prefs 341 | // or some other method in showBookmarksPanel. 342 | // For now, we can just assume that showBookmarksPanel 343 | // is not a consumable API, just a helper 344 | prefs.set("panelVisible", _bookmarksPanel.isOpen()); 345 | } 346 | 347 | /** 348 | * Updates the state from prefs 349 | */ 350 | function updateFromPrefs() { 351 | reloadModel(); 352 | showBookmarksPanel(prefs.get("panelVisible"), prefs.get("viewOptions")); 353 | } 354 | 355 | 356 | // load our styles 357 | ExtensionUtils.loadStyleSheet(module, "styles/styles.css"); 358 | 359 | // register our commands 360 | CommandManager.register(ExtensionStrings.TOGGLE_BOOKMARK, CMD_TOGGLE_BOOKMARK, toggleBookmark); 361 | CommandManager.register(ExtensionStrings.GOTO_PREV_BOOKMARK, CMD_GOTO_PREV_BOOKMARK, _.partial(gotoNextBookmark, false)); 362 | CommandManager.register(ExtensionStrings.GOTO_NEXT_BOOKMARK, CMD_GOTO_NEXT_BOOKMARK, _.partial(gotoNextBookmark, true)); 363 | 364 | // add our menu items 365 | var menu = Menus.getMenu(Menus.AppMenuBar.NAVIGATE_MENU); 366 | 367 | menu.addMenuDivider(); 368 | menu.addMenuItem(CMD_TOGGLE_BOOKMARK, "Ctrl-Shift-K"); 369 | menu.addMenuItem(CMD_GOTO_NEXT_BOOKMARK, "Ctrl-P"); 370 | menu.addMenuItem(CMD_GOTO_PREV_BOOKMARK, "Ctrl-Shift-P"); 371 | 372 | menu = Menus.getMenu(Menus.AppMenuBar.VIEW_MENU); 373 | CommandManager.register(ExtensionStrings.TOGGLE_BOOKMARKS_PANEL, CMD_TOGGLE_BOOKKMARK_VIEW, toggleBookmarksPanel); 374 | menu.addMenuDivider(); 375 | menu.addMenuItem(CMD_TOGGLE_BOOKKMARK_VIEW); 376 | 377 | // define prefs 378 | prefs.definePreference("bookmarks", "object", {}); 379 | prefs.definePreference("panelVisible", "boolean", false); 380 | prefs.definePreference("viewOptions", "object", {}); 381 | 382 | // Initialize 383 | loadModel(); 384 | showBookmarksPanel(prefs.get("panelVisible"), prefs.get("viewOptions")); 385 | 386 | // event handlers 387 | // NOTE: this is an undocumented, unsupported event fired when an editor is created 388 | // @TODO: invent a standard event 389 | EditorManager.on("_fullEditorCreatedForDocument", function (e, document, editor) { 390 | editor.on("beforeDestroy.bookmarks", function () { 391 | saveBookmarks(editor); 392 | editor.off(".bookmarks"); 393 | document.off(".bookmarks"); 394 | }); 395 | document.on("change.bookmarks", function () { 396 | resetBookmarks(editor); 397 | }); 398 | loadBookmarks(editor); 399 | }); 400 | 401 | // prefs change handler, dump everything and reload from prefs 402 | prefs.on("change", updateFromPrefs); 403 | 404 | }); 405 | --------------------------------------------------------------------------------