├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── highlight.png ├── keymaps └── custom-folds.cson ├── lib └── index.js ├── menus └── custom-folds.cson ├── package.json ├── spec ├── custom-folds-spec.coffee └── custom-folds-view-spec.coffee └── styles └── custom-folds.less /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.9.5 2 | Fixes bug with latest version of atom where atom would process the gutter icon click before this plugin. 3 | 4 | ## 1.9.4 5 | Fixes deprecation warning. 6 | Fixes error when preview file and have 'auto-fold' enabled. 7 | 8 | ## 1.9.3 9 | Fixes hack for waiting for file to fully load. 10 | 11 | ## 1.9.2 12 | Fixes deprecations introduced with atom 1.13. 13 | 14 | ## 1.9.1 15 | Fixes bug where gutter icon would not function if region highlighting were disabled. 16 | Fixes bug with the Toggle Fold and Fold Here functions and nested regions. 17 | Fixes bug with cursor offset when folding a region - now your cursor remains on the line so you can toggle back and forth without having to reposition the cursor. 18 | 19 | ## 1.9.0 20 | Adds support for text files. 21 | 22 | ## 1.8.4 23 | Gutter icons only show on hover, matching other foldable regions. 24 | 25 | ## 1.8.3 26 | Fixed bug where in calculating foldable region. 27 | 28 | ## 1.8.1 29 | Fixed bug where foldable regions would sometimes not be highlighted during initial file load. 30 | 31 | ## 1.8.0 32 | Added support for up to three different region tag pairs. 33 | 34 | ## 1.7.0 35 | 0tho added gutter icon for foldable regions. 36 | 37 | ## 1.6.8 38 | Fixed bug where end tag was being displayed on fold instead of start tag. 39 | 40 | ## 1.6.7 41 | Foldable regions no longer require a space, so "//#region" now works. 42 | 43 | ## 1.6.6 44 | Fixed keyboard shortcuts listed in README.md. 45 | 46 | ## 1.6.5 47 | For some syntaxes, such as Babel ES6, the leading whitespace was being underlined. This should now be fixed. 48 | 49 | ## 1.6.4 50 | Added a sleep to fix the 'fold on load' option no longer working with the latest version of Atom. Just a temporary solution until I can figure out the proper way of knowing when it's safe to fold regions. 51 | 52 | ## 1.6.3 53 | Updated the descriptions for the settings. Updated README.md and CHANGELOG.md (to reflect that the last version was acutally 1.6.0 and not 1.5.3). 54 | 55 | ## 1.6.0 56 | Removed the need to add comment characters in the settings for the beginning and end of foldable regions. 57 | Added code to retrieve current grammars comment style to allow for language agnostic region comments. 58 | Added .text selector to stylesheet to enable colorization on html files. 59 | 60 | ## 1.5.2 61 | Find-in-Files works again when 'auto fold on file load' is enabled. Yeah, that was a bad bug. 62 | 63 | ## 1.5.1 64 | Updated README.md. 65 | 66 | ## 1.5.0 67 | Added a setting to automatically fold a file on load. Defaults to false. 68 | 69 | ## 1.3.1 70 | Updated README.md with screenshot. 71 | Added author's name to LICENSE.md. 72 | 73 | ## 1.3.0 74 | Added optional flag for experimental highlighting of folding tags. Folded tags use the @text-color-info color. 75 | Added tags to project. 76 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Brendan Segraves 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Custom Folds 2 | 3 | An Atom plugin for defining custom markers for foldable regions. 4 | 5 | This plugin was inspired by Visual Studio's treatment of C#'s `#region` tags. 6 | 7 | ## Usage 8 | 9 | In the plugin's settings, you can define custom text that identifies the start and end of a foldable section of code. 10 | 11 | By default, a comment starting with `` marks the end of that region. These default settings were only chosen because the author works on a team where a few "special" engineers insist on using JetBrain's WebStorm IDE. These tags allow both sets of engineers (those using Atom and those using WebStorm) to have the same foldable regions of code. 12 | 13 | But you don't need to be working with people that insist on using WebStorm. You can configure the starting and ending tags to whatever you want. This allows you to create your own, custom, collapsible regions. 14 | 15 | The folding is recursive, so you can have regions within regions within regions. 16 | 17 | Highlighting of these foldable tags is also enabled by default. This can be easily disabled from the package's Settings screen. 18 | 19 | ![Image of highlighting](https://github.com/bsegraves/custom-folds/raw/master/highlight.png) 20 | 21 | There is also an option to auto-fold files on load. This can be enabled from the package's settings. 22 | 23 | ### Commands 24 | 25 | * `custom-folds:fold-here` (ctrl-shift-[) — Folds the region you're within. 26 | * `custom-folds:unfold-here` (ctrl-shift-]) — Unfolds the region you're within. 27 | * `custom-folds:fold-all` (ctrl-alt-shift-[) — Folds all regions recursively. 28 | * `custom-folds:unfold-all` (ctrl-alt-shift-]) — Unfolds all regions. 29 | * `custom-folds:fold-top-level` — Only fold the outer regions. 30 | * `custom-folds:toggle-fold` (ctrl-shift-\\) — Toggle folding at the cursor position. 31 | 32 | ## Tips 33 | 34 | Personally I use the following snippet for creating new foldable regions. 35 | 36 | ```json 37 | "editor-fold": 38 | "prefix": "// e" 39 | "body": "// \n// "` 40 | ``` 41 | 42 | ## License 43 | 44 | See [LICENSE](https://github.com/bsegraves/custom-folds/blob/master/LICENSE.md) for details. 45 | -------------------------------------------------------------------------------- /highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsegraves/custom-folds/85b61a07fdd9fbbded450284cff5fbab16b76569/highlight.png -------------------------------------------------------------------------------- /keymaps/custom-folds.cson: -------------------------------------------------------------------------------- 1 | # Keybindings require three things to be fully defined: A selector that is 2 | # matched against the focused element, the keystroke and the command to 3 | # execute. 4 | # 5 | # Below is a basic keybinding which registers on all platforms by applying to 6 | # the root workspace element. 7 | 8 | # For more detailed documentation see 9 | # https://atom.io/docs/latest/behind-atom-keymaps-in-depth 10 | 'atom-workspace': 11 | 'ctrl-shift-[': 'custom-folds:fold-here' 12 | 'ctrl-shift-]': 'custom-folds:unfold-here' 13 | 'ctrl-shift-\\': 'custom-folds:toggle-fold' 14 | 'ctrl-alt-shift-[': 'custom-folds:fold-all' 15 | 'ctrl-alt-shift-]': 'custom-folds:unfold-all' 16 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {CompositeDisposable, Point, Range, TextEditor} from 'atom'; 4 | import {$} from 'atom-space-pen-views'; 5 | 6 | const PREFIX_COUNT = 3; 7 | 8 | // option to auto fold on file open 9 | // clickable region headers, perhaps using block content? 10 | var CustomFolds = 11 | module.exports = { 12 | subscriptions: null, 13 | 14 | config: { 15 | prefix_0: { 16 | title: 'Beginning of first foldable region pair', 17 | description: 'The comment that marks the start of the foldable region must begin with this string literal (not counting leading white space or comment characters).', 18 | type: 'string', 19 | default: '', 27 | order: 2 28 | }, 29 | prefix_1: { 30 | title: 'Beginning of second foldable region pair', 31 | description: 'The comment that marks the start of the foldable region must begin with this string literal (not counting leading white space or comment characters).', 32 | type: 'string', 33 | default: '#region', 34 | order: 3 35 | }, 36 | postfix_1: { 37 | title: 'End of second foldable region pair', 38 | description: 'The comment that marks the end of the foldable region must begin with this string literal (not counting leading white space or comment characters).', 39 | type: 'string', 40 | default: '#endregion', 41 | order: 4 42 | }, 43 | prefix_2: { 44 | title: 'Beginning of third foldable region pair', 45 | description: 'The comment that marks the start of the foldable region must begin with this string literal (not counting leading white space or comment characters).', 46 | type: 'string', 47 | default: '', 48 | order: 5 49 | }, 50 | postfix_2: { 51 | title: 'End of third foldable region pair', 52 | description: 'The comment that marks the end of the foldable region must begin with this string literal (not counting leading white space or comment characters).', 53 | type: 'string', 54 | default: '', 55 | order: 6 56 | }, 57 | areRegionsFoldedOnLoad: { 58 | title: 'Auto fold on file open?', 59 | description: 'If checked, regions start in their folded state when a file is opened.', 60 | type: 'boolean', 61 | default: false, 62 | order: 7 63 | }, 64 | areRegionsHighlighted: { 65 | title: 'Enable region highlighting?', 66 | description: 'If checked, the beginning and end of foldable regions are highlighted.', 67 | type: 'boolean', 68 | default: true, 69 | order: 8 70 | }, 71 | textFilePrefix_0: { 72 | title: 'Beginning of first foldable region pair in text file', 73 | description: `The text that identifies the start of a foldable region in a text file (or any file type that doesn't support comments).`, 74 | type: 'string', 75 | default: '', 83 | order: 10 84 | }, 85 | textFilePrefix_1: { 86 | title: 'Beginning of second foldable region pair in text file', 87 | description: `The text that identifies the start of a foldable region in a text file (or any file type that doesn't support comments).`, 88 | type: 'string', 89 | default: '/*', 90 | order: 11 91 | }, 92 | textFilePostfix_1: { 93 | title: 'End of second foldable region pair in text file', 94 | description: `The text that identifies the end of a foldable region in a text file (or any file type that doesn't support comments).`, 95 | type: 'string', 96 | default: '*/', 97 | order: 12 98 | }, 99 | textFilePrefix_2: { 100 | title: 'Beginning of third foldable region pair in text file', 101 | description: `The text that identifies the start of a foldable region in a text file (or any file type that doesn't support comments).`, 102 | type: 'string', 103 | default: '', 104 | order: 13 105 | }, 106 | textFilePostfix_2: { 107 | title: 'End of third foldable region pair in text file', 108 | description: `The text that identifies the end of a foldable region in a text file (or any file type that doesn't support comments).`, 109 | type: 'string', 110 | default: '', 111 | order: 14 112 | } 113 | }, 114 | 115 | prefixes: [], 116 | postfixes: [], 117 | textFilePrefixes: [], 118 | textFilePostfixes: [], 119 | areRegionsFoldedOnLoad: false, 120 | areRegionsHighlighted: true, 121 | 122 | editors: [], 123 | editorIdToMarkers: {}, 124 | 125 | // LIFE ****************************************************** 126 | activate() { 127 | this.subscriptions = new CompositeDisposable(); 128 | this.subscriptions.add( 129 | atom.commands.add('atom-workspace', { 130 | 'custom-folds:fold-top-level': CustomFolds.foldTopLevel, 131 | 'custom-folds:fold-all': CustomFolds.foldAll, 132 | 'custom-folds:unfold-all': CustomFolds.unfoldAll, 133 | 'custom-folds:fold-here': CustomFolds.foldHere, 134 | 'custom-folds:unfold-here': CustomFolds.unfoldHere, 135 | 'custom-folds:toggle-fold': CustomFolds.toggleFold 136 | })); 137 | 138 | for (let c=0; c { 147 | CustomFolds.prefixes[index] = change.newValue; 148 | CustomFolds._updateHighlightsAcrossEditors(); 149 | }); 150 | 151 | CustomFolds.postfixes.push(postfix); 152 | atom.config.onDidChange(postfixPath, change => { 153 | CustomFolds.postfixes[index] = change.newValue; 154 | CustomFolds._updateHighlightsAcrossEditors(); 155 | }); 156 | } 157 | 158 | for (let c=0; c { 167 | CustomFolds.textFilePrefixes[index] = change.newValue; 168 | CustomFolds._updateHighlightsAcrossEditors(); 169 | }); 170 | 171 | CustomFolds.textFilePostfixes.push(postfix); 172 | atom.config.onDidChange(postfixPath, change => { 173 | CustomFolds.textFilePostfixes[index] = change.newValue; 174 | CustomFolds._updateHighlightsAcrossEditors(); 175 | }); 176 | } 177 | 178 | CustomFolds.areRegionsFoldedOnLoad = atom.config.get('custom-folds.areRegionsFoldedOnLoad'); 179 | atom.config.onDidChange('custom-folds.areRegionsFoldedOnLoad', change => { 180 | CustomFolds.areRegionsFoldedOnLoad = change.newValue; 181 | }); 182 | 183 | CustomFolds.areRegionsHighlighted = atom.config.get('custom-folds.areRegionsHighlighted'); 184 | atom.config.onDidChange('custom-folds.areRegionsHighlighted', change => { 185 | CustomFolds.areRegionsHighlighted = change.newValue; 186 | CustomFolds._updateHighlightsAcrossEditors(); 187 | }); 188 | 189 | this.subscriptions.add(atom.workspace.observeTextEditors(editor => { 190 | CustomFolds.editors.push(editor); 191 | CustomFolds.editorIdToMarkers[editor.id] = []; 192 | 193 | // Delaying two animation frames seems to fix the issue of the empty comment chars. 194 | // This is a really stupid fix. Would prefer an event to properly signal this but I can't find such an event in the atom api. 195 | window.requestAnimationFrame(() => { 196 | window.requestAnimationFrame(() => { 197 | CustomFolds._updateHighlights(editor); 198 | }); 199 | }); 200 | CustomFolds._addClickEvent(editor); 201 | 202 | // It's easier just to always subscribe to this. 203 | editor.onDidStopChanging(() => CustomFolds._updateHighlights(editor)); 204 | })); 205 | 206 | this.subscriptions.add(atom.workspace.onDidAddTextEditor(event => { 207 | if (CustomFolds.areRegionsFoldedOnLoad) { 208 | event.textEditor.tokenizedBuffer.onDidTokenize(() => { 209 | CustomFolds.foldAll(event.textEditor); 210 | }); 211 | } 212 | })); 213 | }, 214 | 215 | deactivate() { 216 | this.subscriptions.dispose(); 217 | }, 218 | // life 219 | 220 | // RESPONDERS ************************************************ 221 | foldTopLevel() { 222 | const options = CustomFolds._getOptions(); 223 | 224 | for (let c=0, cLen=options.editor.getLineCount(); c { 229 | const postfix = options.postfixes[index]; 230 | if (!prefix || !prefix.length || !postfix || !postfix.length) { return; } 231 | 232 | if (line.replace(options.commentChars,'').trim().startsWith(prefix)) { 233 | const endRow = CustomFolds._rowOfEndTag(options, index, c); 234 | if (c < endRow) { 235 | CustomFolds._fold(options.editor, c, endRow); 236 | c = endRow + 1; 237 | } 238 | } 239 | }); 240 | } 241 | } 242 | }, 243 | 244 | foldAll(currentEditor=undefined) { 245 | // When called from keyboard binding, some arguments get sent in. We're only interested if the argument is the editor. 246 | if (!(currentEditor instanceof TextEditor)) { 247 | currentEditor = undefined; 248 | } 249 | const {editor, commentChars, areCommentsRequired, prefixes, postfixes} = CustomFolds._getOptions(currentEditor); 250 | 251 | let startPrefixStack = []; 252 | for (let c=0, cLen=editor.getLineCount(); c { 256 | const postfix = postfixes[index]; 257 | if (!prefix || !prefix.length || !postfix || !postfix.length) { return; } 258 | 259 | const trimmedLine = line.replace(commentChars,'').trim(); 260 | if (trimmedLine.startsWith(prefix)) { 261 | startPrefixStack.push(c); 262 | } else if (trimmedLine.startsWith(postfix)) { 263 | if (startPrefixStack.length) { 264 | const startRow = startPrefixStack.pop(); 265 | CustomFolds._fold(editor, startRow, c); 266 | } else { 267 | atom.notifications.addWarning(`Extra closing fold tag found at line ${c + 1}.`); 268 | } 269 | } 270 | }); 271 | } 272 | } 273 | 274 | if (startPrefixStack.length) { 275 | atom.notifications.addWarning(`Extra opening fold tag found at line ${startPrefixStack.pop() + 1}.`); 276 | } 277 | }, 278 | 279 | unfoldAll() { 280 | const {editor, commentChars, areCommentsRequired, prefixes} = CustomFolds._getOptions(); 281 | 282 | for (let c=0, cLen=editor.getLineCount(); c { 287 | if (!prefix || !prefix.length) { return; } 288 | 289 | if (line.replace(commentChars,'').trim().startsWith(prefix)) { 290 | editor.unfoldBufferRow(c); 291 | } 292 | }); 293 | } 294 | } 295 | }, 296 | 297 | // TODO: do this for each cursor 298 | foldHere() { 299 | const options = CustomFolds._getOptions(); 300 | 301 | const row = CustomFolds._rowOfStartTag(options, options.editor.getCursorBufferPosition().row); 302 | if (row >= 0) { 303 | CustomFolds.foldRow(row, options); 304 | } 305 | }, 306 | 307 | foldRow(row, options=undefined) { 308 | let result = false; 309 | 310 | options = options || CustomFolds._getOptions(); 311 | 312 | row = +row; // Ensure row is a number 313 | const line = options.editor.lineTextForBufferRow(row).trim(); 314 | if (!options.areCommentsRequired || options.editor.isBufferRowCommented(row)) { 315 | for (let c=0, cLen=options.prefixes.length; c responders 352 | 353 | // HELPERS *************************************************** 354 | _fold(editor, startRow, endRow) { 355 | editor.setSelectedBufferRange(new Range(new Point(startRow, 512), new Point(endRow, 512))); 356 | editor.foldSelectedLines(); 357 | }, 358 | 359 | // Fetch the current editor, applicable prefix and postfix arrays, as well as the commentChars 360 | _getOptions(editor=undefined) { 361 | editor = editor || atom.workspace.getActiveTextEditor(); 362 | const commentChars = editor ? (atom.config.get('editor.commentStart', {scope: editor.getRootScopeDescriptor()}) || '').trim() : ''; 363 | const areCommentsRequired = commentChars !== ''; 364 | const prefixes = areCommentsRequired ? CustomFolds.prefixes : CustomFolds.textFilePrefixes; 365 | const postfixes = areCommentsRequired ? CustomFolds.postfixes : CustomFolds.textFilePostfixes; 366 | 367 | return {editor, commentChars, areCommentsRequired, prefixes, postfixes}; 368 | }, 369 | 370 | // takes nesting into account 371 | _rowOfStartTag(options, startRow) { 372 | let result = -1; 373 | 374 | let endTagCount = 1; 375 | for (let c=startRow; c>=0 && endTagCount>0; --c) { 376 | const line = options.editor.lineTextForBufferRow(c).trim(); 377 | if (!options.areCommentsRequired || options.editor.isBufferRowCommented(c)) { 378 | for (let d=0, dLen=options.prefixes.length; d m.destroy()); 442 | 443 | const {commentChars, areCommentsRequired, prefixes, postfixes} = CustomFolds._getOptions(editor); 444 | // atom.notifications.addWarning(`CommmentChars: "${commentChars}".`); 445 | 446 | for (let c=0, cLen=editor.getLineCount(); c { 450 | const postfix = postfixes[index]; 451 | if (!prefix || !prefix.length || !postfix || !postfix.length) { return; } 452 | 453 | let cls; 454 | if (line.replace(commentChars,'').trim().startsWith(prefix)) { 455 | cls = 'custom-folds-start'; 456 | } else if (line.replace(commentChars,'').trim().startsWith(postfix)) { 457 | cls = 'custom-folds-stop'; 458 | } 459 | 460 | if (cls) { 461 | let range = [[c,0],[c,0]]; 462 | let marker = editor.markBufferRange(range); 463 | markers.push(marker); 464 | editor.decorateMarker(marker, {type: 'line', class: cls}); 465 | if (cls === 'custom-folds-start') { 466 | editor.decorateMarker(marker, {type: 'line-number', class: cls}); 467 | } 468 | 469 | if (!CustomFolds.areRegionsHighlighted) { 470 | editor.decorateMarker(marker, {type: 'line', class: 'no-highlight'}); 471 | if (cls === 'custom-folds-start') { 472 | editor.decorateMarker(marker, {type: 'line-number', class: 'no-highlight'}); 473 | } 474 | } 475 | } 476 | }); 477 | } 478 | }, 479 | 480 | _updateHighlightsAcrossEditors() { 481 | CustomFolds.editors.forEach(CustomFolds._updateHighlights); 482 | }, 483 | 484 | _addClickEvent(editor) { 485 | const editorView = atom.views.getView(editor); 486 | const gutter = editorView.querySelector('.gutter'); 487 | $(gutter).on('mousedown', '.line-number.custom-folds-start:not(.folded) .icon-right', function(event) { 488 | const row = event.target.parentElement.dataset.bufferRow; 489 | CustomFolds.foldRow(row); 490 | }); 491 | } 492 | // helpers 493 | }; 494 | 495 | 496 | 497 | // For testing… 498 | 499 | // 500 | // 501 | // 502 | // 503 | // 504 | // 505 | -------------------------------------------------------------------------------- /menus/custom-folds.cson: -------------------------------------------------------------------------------- 1 | 'context-menu': 2 | 'atom-text-editor': [ 3 | { 4 | 'label': 'Custom Folds' 5 | 'submenu': [ 6 | { 7 | 'label': 'Fold Top Level' 8 | 'command': 'custom-folds:fold-top-level' 9 | }, 10 | { 11 | 'label': 'Fold All' 12 | 'command': 'custom-folds:fold-all' 13 | }, 14 | { 15 | 'label': 'Unfold All' 16 | 'command': 'custom-folds:unfold-all' 17 | }, 18 | { 19 | 'label': 'Fold Here' 20 | 'command': 'custom-folds:fold-here' 21 | }, 22 | { 23 | 'label': 'Unfold Here' 24 | 'command': 'custom-folds:unfold-here' 25 | }, 26 | { 27 | 'label': 'Toggle Fold' 28 | 'command': 'custom-folds:toggle-fold' 29 | } 30 | ] 31 | } 32 | ] 33 | 'menu': [ 34 | { 35 | 'label': 'Packages' 36 | 'submenu': [ 37 | 'label': 'Custom Folds' 38 | 'submenu': [ 39 | { 40 | 'label': 'Fold Top Level' 41 | 'command': 'custom-folds:fold-top-level' 42 | }, 43 | { 44 | 'label': 'Fold All' 45 | 'command': 'custom-folds:fold-all' 46 | }, 47 | { 48 | 'label': 'Unfold All' 49 | 'command': 'custom-folds:unfold-all' 50 | }, 51 | { 52 | 'label': 'Fold Here' 53 | 'command': 'custom-folds:fold-here' 54 | }, 55 | { 56 | 'label': 'Unfold Here' 57 | 'command': 'custom-folds:unfold-here' 58 | }, 59 | { 60 | 'label': 'Toggle Fold' 61 | 'command': 'custom-folds:toggle-fold' 62 | } 63 | ] 64 | ] 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-folds", 3 | "main": "./lib/index.js", 4 | "version": "1.9.5", 5 | "description": "Define custom tags for defining foldable blocks of code.", 6 | "keywords": [ 7 | "fold", 8 | "WebStorm", 9 | "#region", 10 | "editor-fold" 11 | ], 12 | "repository": "https://github.com/bsegraves/custom-folds", 13 | "license": "MIT", 14 | "engines": { 15 | "atom": ">=1.0.0 <2.0.0" 16 | }, 17 | "dependencies": { 18 | "atom-space-pen-views": "^2.0.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/custom-folds-spec.coffee: -------------------------------------------------------------------------------- 1 | CustomFolds = require '../lib/custom-folds' 2 | 3 | # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 4 | # 5 | # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 6 | # or `fdescribe`). Remove the `f` to unfocus the block. 7 | 8 | describe "CustomFolds", -> 9 | [workspaceElement, activationPromise] = [] 10 | 11 | beforeEach -> 12 | workspaceElement = atom.views.getView(atom.workspace) 13 | activationPromise = atom.packages.activatePackage('custom-folds') 14 | 15 | describe "when the custom-folds:toggle event is triggered", -> 16 | it "hides and shows the modal panel", -> 17 | # Before the activation event the view is not on the DOM, and no panel 18 | # has been created 19 | expect(workspaceElement.querySelector('.custom-folds')).not.toExist() 20 | 21 | # This is an activation event, triggering it will cause the package to be 22 | # activated. 23 | atom.commands.dispatch workspaceElement, 'custom-folds:toggle' 24 | 25 | waitsForPromise -> 26 | activationPromise 27 | 28 | runs -> 29 | expect(workspaceElement.querySelector('.custom-folds')).toExist() 30 | 31 | customFoldsElement = workspaceElement.querySelector('.custom-folds') 32 | expect(customFoldsElement).toExist() 33 | 34 | customFoldsPanel = atom.workspace.panelForItem(customFoldsElement) 35 | expect(customFoldsPanel.isVisible()).toBe true 36 | atom.commands.dispatch workspaceElement, 'custom-folds:toggle' 37 | expect(customFoldsPanel.isVisible()).toBe false 38 | 39 | it "hides and shows the view", -> 40 | # This test shows you an integration test testing at the view level. 41 | 42 | # Attaching the workspaceElement to the DOM is required to allow the 43 | # `toBeVisible()` matchers to work. Anything testing visibility or focus 44 | # requires that the workspaceElement is on the DOM. Tests that attach the 45 | # workspaceElement to the DOM are generally slower than those off DOM. 46 | jasmine.attachToDOM(workspaceElement) 47 | 48 | expect(workspaceElement.querySelector('.custom-folds')).not.toExist() 49 | 50 | # This is an activation event, triggering it causes the package to be 51 | # activated. 52 | atom.commands.dispatch workspaceElement, 'custom-folds:toggle' 53 | 54 | waitsForPromise -> 55 | activationPromise 56 | 57 | runs -> 58 | # Now we can test for view visibility 59 | customFoldsElement = workspaceElement.querySelector('.custom-folds') 60 | expect(customFoldsElement).toBeVisible() 61 | atom.commands.dispatch workspaceElement, 'custom-folds:toggle' 62 | expect(customFoldsElement).not.toBeVisible() 63 | -------------------------------------------------------------------------------- /spec/custom-folds-view-spec.coffee: -------------------------------------------------------------------------------- 1 | CustomFoldsView = require '../lib/custom-folds-view' 2 | 3 | describe "CustomFoldsView", -> 4 | it "has one valid test", -> 5 | expect("life").toBe "easy" 6 | -------------------------------------------------------------------------------- /styles/custom-folds.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | atom-text-editor { 8 | .custom-folds-start:not(.no-highlight) { 9 | .syntax--source, .syntax--text { 10 | .syntax--comment.syntax--block, 11 | .syntax--comment.syntax--definition, 12 | .syntax--comment.syntax--line { 13 | color: @text-color-info; 14 | border-top: 1px solid @text-color-info; 15 | 16 | .syntax--leading-whitespace { 17 | border-top: 1px solid @pane-item-background-color; 18 | } 19 | } 20 | } 21 | 22 | .syntax--text.syntax--plain { 23 | color: @text-color-info; 24 | border-top: 1px solid @text-color-info; 25 | 26 | &.syntax--leading-whitespace, 27 | .syntax--leading-whitespace { 28 | border-top: 1px solid @pane-item-background-color; 29 | } 30 | } 31 | } 32 | 33 | .custom-folds-stop:not(.no-highlight) { 34 | .syntax--source, .syntax--text { 35 | .syntax--comment.syntax--block, 36 | .syntax--comment.syntax--definition, 37 | .syntax--comment.syntax--line { 38 | color: @text-color-info; 39 | border-bottom: 1px solid @text-color-info; 40 | 41 | .syntax--leading-whitespace { 42 | border-bottom: 1px solid @pane-item-background-color; 43 | } 44 | } 45 | } 46 | 47 | .syntax--text.syntax--plain { 48 | color: @text-color-info; 49 | border-bottom: 1px solid @text-color-info; 50 | 51 | &.syntax--leading-whitespace, 52 | .syntax--leading-whitespace { 53 | border-bottom: 1px solid @pane-item-background-color; 54 | } 55 | } 56 | } 57 | 58 | .gutter:hover { 59 | .line-number.custom-folds-start .icon-right { 60 | visibility: visible; 61 | 62 | &:hover { 63 | opacity: 1; 64 | } 65 | } 66 | } 67 | } 68 | --------------------------------------------------------------------------------