├── .gitignore ├── images ├── icon.png ├── screenshot.png └── octicons │ ├── light │ ├── pencil.svg │ ├── book.svg │ └── list-unordered.svg │ └── dark │ ├── pencil.svg │ ├── book.svg │ └── list-unordered.svg ├── .vscodeignore ├── .editorconfig ├── tsconfig.json ├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── README.md ├── package.json └── src └── extension.ts /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | *.vsix 4 | vsc-extension-quickstart.md 5 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spywhere/vscode-mark-jump/HEAD/images/icon.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spywhere/vscode-mark-jump/HEAD/images/screenshot.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | typings/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | *.vsix 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": ["es6"], 7 | "sourceMap": true 8 | }, 9 | "exclude": [ 10 | "node_modules", 11 | ".vscode-test" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | "typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version 10 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.1.0", 4 | "configurations": [ 5 | { 6 | "name": "Launch Extension", 7 | "type": "extensionHost", 8 | "request": "launch", 9 | "runtimeExecutable": "${execPath}", 10 | "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], 11 | "stopOnEntry": false, 12 | "sourceMaps": true, 13 | "outDir": "${workspaceRoot}/out", 14 | "preLaunchTask": "npm" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /images/octicons/light/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pencil 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /images/octicons/dark/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pencil 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /images/octicons/light/book.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | book 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /images/octicons/dark/book.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | book 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls a custom npm script that compiles the extension. 10 | { 11 | "version": "0.1.0", 12 | 13 | // we want to run npm 14 | "command": "npm", 15 | 16 | // the command is a shell script 17 | "isShellCommand": true, 18 | 19 | // show the output window only if unrecognized errors occur. 20 | "showOutput": "silent", 21 | 22 | // we run the custom script "compile" as defined in package.json 23 | "args": ["run", "compile", "--loglevel", "silent"], 24 | 25 | // The tsc compiler is started in watching mode 26 | "isWatching": true, 27 | 28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 29 | "problemMatcher": "$tsc-watch" 30 | } -------------------------------------------------------------------------------- /images/octicons/light/list-unordered.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | list-unordered 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /images/octicons/dark/list-unordered.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | list-unordered 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Mark Jump 2 | [![Version](https://vsmarketplacebadge.apphb.com/version/spywhere.mark-jump.svg)](https://marketplace.visualstudio.com/items?itemName=spywhere.mark-jump) 3 | [![Installs](https://vsmarketplacebadge.apphb.com/installs/spywhere.mark-jump.svg)](https://marketplace.visualstudio.com/items?itemName=spywhere.mark-jump) 4 | 5 | Jump to the marked section in the code 6 | 7 | ![Screenshot](images/screenshot.png) 8 | 9 | ### What is Mark Jump? 10 | Mark Jump is simply an extension that let you jump across your marking points in the code. 11 | 12 | ### How to use it? 13 | Simply install the extension, Mark Jump should show the mark count in the status bar! 14 | Use arrow keys to jump between them, click to jump to it. 15 | 16 | You can also use the following key bindings to jump through various type of marks... 17 | 18 | - `Ctrl+Alt+P` / `Ctrl+Cmd+P` (`markJump.jumpToProjectMarks`): Jump to all marks in the project 19 | - `Ctrl+Alt+M` / `Ctrl+Cmd+M` (`markJump.jumpToMarks`): Jump to all marks (either in current editor or a whole project) 20 | - `Ctrl+Alt+S` / `Ctrl+Cmd+S` (`markJump.jumpToEditorMarks.section`): Jump to all section marks (in current editor) 21 | - `Ctrl+Alt+T` / `Ctrl+Cmd+T` (`markJump.jumpToEditorMarks.todo`): Jump to all TODOs (in current editor) 22 | - `Ctrl+Alt+N` / `Ctrl+Cmd+N` (`markJump.jumpToEditorMarks.note`): Jump to all Notes (in current editor) 23 | 24 | - `Ctrl+Alt+,` / `Ctrl+Cmd+,` (`markJump.jumpToPreviousMark`): Jump to previous mark (in current editor) 25 | - `Ctrl+Alt+.` / `Ctrl+Cmd+.` (`markJump.jumpToNextMark`): Jump to next mark (in current editor) 26 | 27 | ### How to write the marks? 28 | Just simply write one of these syntax in your code, Mark Jump will found it right away! 29 | 30 | You can also add more syntax via the configurations (pull requests are welcomed). 31 | 32 | #### Section Marks 33 | - `// MARK: Section name` 34 | - `# pragma Section name` (case sensitive) 35 | 36 | #### TODOs 37 | - `// TODO: Text goes here` 38 | - `// TODO(writer name): Text goes here` 39 | 40 | #### Notes 41 | - `// NOTE: Text goes here` 42 | - `// NOTE(writer name): Text goes here` 43 | 44 | #### Mark Filter 45 | You can custom how "Jump to previous/next mark" jump by use a specific command instead of general one. 46 | 47 | - To filter only "Section" mark, add `.section` after a command name. 48 | - To filter only "To Do" mark, add `.todo` after a command name. 49 | - To filter only "Note" mark, add `.note` after a command name. 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mark-jump", 3 | "displayName": "Mark Jump", 4 | "description": "Jump to the marked section in the code", 5 | "version": "0.9.0", 6 | "publisher": "spywhere", 7 | "icon": "images/icon.png", 8 | "bugs": { 9 | "url": "https://github.com/spywhere/vscode-mark-jump/issues" 10 | }, 11 | "homepage": "https://github.com/spywhere/vscode-mark-jump/blob/master/README.md", 12 | "keywords": [ 13 | "marker", 14 | "section", 15 | "jump", 16 | "comment" 17 | ], 18 | "license": "MIT", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/spywhere/vscode-mark-jump.git" 22 | }, 23 | "engines": { 24 | "vscode": "^1.17.0" 25 | }, 26 | "categories": [ 27 | "Other" 28 | ], 29 | "activationEvents": [ 30 | "*" 31 | ], 32 | "main": "./out/extension", 33 | "contributes": { 34 | "views": { 35 | "explorer": [ 36 | { 37 | "id": "markJump", 38 | "name": "Mark Jump", 39 | "when": "config.markJump.showExplorerView == true" 40 | } 41 | ] 42 | }, 43 | "commands": [ 44 | { 45 | "command": "markJump.jumpToProjectMarks", 46 | "title": "Mark Jump: Jump to Project-wide Marks..." 47 | }, 48 | { 49 | "command": "markJump.jumpToMarks", 50 | "title": "Mark Jump: Jump to Marks..." 51 | }, 52 | { 53 | "command": "markJump.jumpToEditorMarks.section", 54 | "title": "Mark Jump: Jump to Sections..." 55 | }, 56 | { 57 | "command": "markJump.jumpToEditorMarks.todo", 58 | "title": "Mark Jump: Jump to TODOs..." 59 | }, 60 | { 61 | "command": "markJump.jumpToEditorMarks.note", 62 | "title": "Mark Jump: Jump to Notes..." 63 | } 64 | ], 65 | "keybindings": [ 66 | { 67 | "command": "markJump.jumpToProjectMarks", 68 | "key": "ctrl+alt+p", 69 | "mac": "ctrl+cmd+p" 70 | }, 71 | { 72 | "command": "markJump.jumpToMarks", 73 | "key": "ctrl+alt+m", 74 | "mac": "ctrl+cmd+m" 75 | }, 76 | { 77 | "command": "markJump.jumpToEditorMarks.section", 78 | "when": "editorTextFocus", 79 | "key": "ctrl+alt+s", 80 | "mac": "ctrl+cmd+s" 81 | }, 82 | { 83 | "command": "markJump.jumpToEditorMarks.todo", 84 | "when": "editorTextFocus", 85 | "key": "ctrl+alt+t", 86 | "mac": "ctrl+cmd+t" 87 | }, 88 | { 89 | "command": "markJump.jumpToEditorMarks.note", 90 | "when": "editorTextFocus", 91 | "key": "ctrl+alt+n", 92 | "mac": "ctrl+cmd+n" 93 | }, 94 | { 95 | "command": "markJump.jumpToPreviousMark", 96 | "when": "editorTextFocus", 97 | "key": "ctrl+alt+,", 98 | "mac": "ctrl+cmd+," 99 | }, 100 | { 101 | "command": "markJump.jumpToNextMark", 102 | "when": "editorTextFocus", 103 | "key": "ctrl+alt+.", 104 | "mac": "ctrl+cmd+." 105 | } 106 | ], 107 | "configuration": { 108 | "type": "object", 109 | "title": "Mark Jump Configurations", 110 | "properties": { 111 | "markJump.alwaysOpenDocument": { 112 | "type": "boolean", 113 | "default": true, 114 | "description": "Automatically open and show a document according to the marks.", 115 | "scope": "window" 116 | }, 117 | "markJump.headingSymbol": { 118 | "type": "string", 119 | "default": "└─", 120 | "description": "Heading symbol to indicate multiple level of marks.", 121 | "scope": "window" 122 | }, 123 | "markJump.testPatterns": { 124 | "type": "array", 125 | "default": [ 126 | "//\\s*(\\>+)?\\s*[Mm][Aa][Rr][Kk]", 127 | "#\\s*(\\>+)?\\s*pragma", 128 | "//\\s*(\\>+)?\\s*[Tt][Oo][Dd][Oo]", 129 | "//\\s*(\\>+)?\\s*[Ff][Ii][Xx][Mm][Ee]", 130 | "//\\s*(\\>+)?\\s*[Nn][Oo][Tt][Ee]" 131 | ], 132 | "items": { 133 | "type": "string" 134 | }, 135 | "description": "A list of regular patterns to test the file (when match, the whole file will be inspected).", 136 | "scope": "window" 137 | }, 138 | "markJump.additionalTestPatterns": { 139 | "type": "array", 140 | "default": [ 141 | "//\\s*(\\>+)?\\s*[Mm][Aa][Rr][Kk]", 142 | "#\\s*(\\>+)?\\s*pragma", 143 | "//\\s*(\\>+)?\\s*[Tt][Oo][Dd][Oo]", 144 | "//\\s*(\\>+)?\\s*[Ff][Ii][Xx][Mm][Ee]", 145 | "//\\s*(\\>+)?\\s*[Nn][Oo][Tt][Ee]" 146 | ], 147 | "items": { 148 | "type": "string" 149 | }, 150 | "description": "Additional list of regular patterns to test the file. Use this configuration to avoid replacing default patterns.", 151 | "scope": "window" 152 | }, 153 | "markJump.sectionPatterns": { 154 | "type": "array", 155 | "default": [ 156 | "//\\s*(?\\>+)?\\s*[Mm][Aa][Rr][Kk]\\s*:\\s*(?.+)$", 157 | "#\\s*(?\\>+)?\\s*pragma\\s+(?.+)$" 158 | ], 159 | "items": { 160 | "type": "string" 161 | }, 162 | "description": "A list of regular patterns to match the section.", 163 | "scope": "window" 164 | }, 165 | "markJump.additionalSectionPatterns": { 166 | "type": "array", 167 | "default": [], 168 | "items": { 169 | "type": "string" 170 | }, 171 | "description": "Additional list of regular pattern to match the section. Use this configuration to avoid replacing default patterns.", 172 | "scope": "window" 173 | }, 174 | "markJump.todoPatterns": { 175 | "type": "array", 176 | "default": [ 177 | "//\\s*(?\\>+)?\\s*[Tt][Oo][Dd][Oo]\\s*(\\((?[^\\)]+)\\))?\\s*:\\s*(?.+)$", 178 | "//\\s*(?\\>+)?\\s*[Ff][Ii][Xx][Mm][Ee]\\s*(\\((?[^\\)]+)\\))?\\s*:\\s*(?.+)$" 179 | ], 180 | "items": { 181 | "type": "string" 182 | }, 183 | "description": "A list of regular pattern to match the TODOs.", 184 | "scope": "window" 185 | }, 186 | "markJump.additionalTODOPatterns": { 187 | "type": "array", 188 | "default": [], 189 | "items": { 190 | "type": "string" 191 | }, 192 | "description": "Additional list of regular pattern to match the TODOs. Use this configuration to avoid replacing default patterns.", 193 | "scope": "window" 194 | }, 195 | "markJump.notePatterns": { 196 | "type": "array", 197 | "default": [ 198 | "//\\s*(?\\>+)?\\s*[Nn][Oo][Tt][Ee]\\s*(\\((?[^\\)]+)\\))?\\s*:\\s*(?.+)$" 199 | ], 200 | "items": { 201 | "type": "string" 202 | }, 203 | "description": "A list of regular pattern to match the notes.", 204 | "scope": "window" 205 | }, 206 | "markJump.additionalNotePatterns": { 207 | "type": "array", 208 | "default": [], 209 | "items": { 210 | "type": "string" 211 | }, 212 | "description": "Additional list of regular pattern to match the notes. Use this configuration to avoid replacing default patterns.", 213 | "scope": "window" 214 | }, 215 | "markJump.showProjectMarks": { 216 | "type": "boolean", 217 | "default": true, 218 | "description": "Show project-wide marks on the status bar.", 219 | "scope": "window" 220 | }, 221 | "markJump.includeFilePattern": { 222 | "type": "string", 223 | "default": "**/*", 224 | "description": "Glob pattern for files to be included in project marks.", 225 | "scope": "window" 226 | }, 227 | "markJump.excludeFilePattern": { 228 | "type": "string", 229 | "default": "**/node_modules/**", 230 | "description": "Glob pattern for files to be excluded in project marks.", 231 | "scope": "window" 232 | }, 233 | "markJump.highlightColor.dark": { 234 | "type": "string", 235 | "default": "rgba(60, 60, 60, 0.75)", 236 | "description": "Highlight rendering color for dark themes.", 237 | "scope": "window" 238 | }, 239 | "markJump.highlightColor.light": { 240 | "type": "string", 241 | "default": "rgba(220, 220, 220, 0.75)", 242 | "description": "Highlight rendering color for light themes.", 243 | "scope": "window" 244 | }, 245 | "markJump.showStatusItem": { 246 | "type": "boolean", 247 | "default": true, 248 | "description": "Show numbers of marks in the status bar.", 249 | "scope": "window" 250 | }, 251 | "markJump.showExplorerView": { 252 | "type": "boolean", 253 | "default": true, 254 | "description": "Show marks in the explorer viewlet.", 255 | "scope": "window" 256 | }, 257 | "markJump.maximumLimit": { 258 | "type": "number", 259 | "default": 30, 260 | "description": "Show warning when a number of matched files exceed this limit. Set to -1 for unlimited.", 261 | "scope": "window" 262 | }, 263 | "markJump.strictLimit": { 264 | "type": "string", 265 | "default": "none", 266 | "description": "Keep the maximum limit as is.\n- \"none\" to show warnings and suggestions\n- \"disable\" to disable\n- \"limit\" to limit to the specified limit.", 267 | "enum": [ 268 | "none", 269 | "disable", 270 | "limit" 271 | ], 272 | "scope": "window" 273 | } 274 | } 275 | } 276 | }, 277 | "scripts": { 278 | "vscode:prepublish": "tsc -p ./", 279 | "compile": "tsc -watch -p ./", 280 | "postinstall": "node ./node_modules/vscode/bin/install" 281 | }, 282 | "devDependencies": { 283 | "@types/node": "^8.0.53", 284 | "@types/xregexp": "^3.0.29", 285 | "typescript": "^2.6.1", 286 | "vscode": "^1.1.36" 287 | }, 288 | "dependencies": { 289 | "xregexp": "^3.2.0" 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import * as vscode from "vscode"; 3 | import * as XRegExp from "xregexp"; 4 | import * as fs from "fs"; 5 | import * as path from "path"; 6 | 7 | export function activate(context: vscode.ExtensionContext) { 8 | let markJump = new MarkJump(); 9 | let treeProvider = new MarkJumpTreeProvider(markJump, context); 10 | context.subscriptions.push(markJump); 11 | context.subscriptions.push(vscode.window.registerTreeDataProvider( 12 | "markJump", treeProvider 13 | )); 14 | context.subscriptions.push(new MarkJumpController(markJump, treeProvider)); 15 | } 16 | 17 | interface ActionItem extends vscode.MessageItem { 18 | danger?: { 19 | title: string; 20 | action: string; 21 | cancel: string; 22 | }; 23 | action?: () => (Thenable | number); 24 | } 25 | 26 | class MarkJumpController { 27 | private markJump: MarkJump; 28 | private treeProvider: MarkJumpTreeProvider; 29 | private disposable: vscode.Disposable; 30 | private lastLine: number = undefined; 31 | 32 | constructor(markJump: MarkJump, treeProvider: MarkJumpTreeProvider){ 33 | this.markJump = markJump; 34 | this.treeProvider = treeProvider; 35 | 36 | let subscriptions: vscode.Disposable[] = []; 37 | subscriptions.push(vscode.commands.registerCommand( 38 | "markJump.jumpToProjectMarks", () => { 39 | this.markJump.jumpToEditorMark( 40 | undefined, undefined, "section", "todo", "note" 41 | ); 42 | } 43 | )); 44 | subscriptions.push(vscode.commands.registerCommand( 45 | "markJump.jumpToMarks", () => { 46 | this.markJump.jumpToMark(true, "section", "todo", "note"); 47 | } 48 | )); 49 | subscriptions.push(vscode.commands.registerCommand( 50 | "markJump.jumpToEditorMarks", () => { 51 | this.markJump.jumpToMark(false, "section", "todo", "note"); 52 | } 53 | )); 54 | subscriptions.push(vscode.commands.registerCommand( 55 | "markJump.jumpToPreviousMark", () => { 56 | this.markJump.jumpToPreviousMark("section", "todo", "note"); 57 | } 58 | )); 59 | subscriptions.push(vscode.commands.registerCommand( 60 | "markJump.jumpToNextMark", () => { 61 | this.markJump.jumpToNextMark("section", "todo", "note"); 62 | } 63 | )); 64 | ["section", "todo", "note"].forEach((type) => { 65 | subscriptions.push(vscode.commands.registerCommand( 66 | `markJump.jumpToProjectMarks.${ type }`, () => { 67 | this.markJump.jumpToEditorMark( 68 | undefined, undefined, type 69 | ); 70 | } 71 | )); 72 | subscriptions.push(vscode.commands.registerCommand( 73 | `markJump.jumpToMarks.${ type }`, () => { 74 | this.markJump.jumpToMark(true, type); 75 | } 76 | )); 77 | subscriptions.push(vscode.commands.registerCommand( 78 | `markJump.jumpToEditorMarks.${ type }`, () => { 79 | this.markJump.jumpToMark(false, type); 80 | } 81 | )); 82 | subscriptions.push(vscode.commands.registerCommand( 83 | `markJump.jumpToPreviousMark.${ type }`, () => { 84 | this.markJump.jumpToPreviousMark(type); 85 | } 86 | )); 87 | subscriptions.push(vscode.commands.registerCommand( 88 | `markJump.jumpToNextMark.${ type }`, () => { 89 | this.markJump.jumpToNextMark(type); 90 | } 91 | )); 92 | }); 93 | subscriptions.push(vscode.commands.registerCommand( 94 | "markJump.revealMark", (mark: BaseMarkItem) => { 95 | this.markJump.openAndRevealMark(mark); 96 | } 97 | )); 98 | this.markJump.createStatusBar(); 99 | vscode.workspace.onDidOpenTextDocument(document => { 100 | this.markJump.updateStatusBar(false); 101 | this.treeProvider.refresh(); 102 | }, this, subscriptions); 103 | vscode.workspace.onDidCloseTextDocument(document => { 104 | this.lastLine = undefined; 105 | this.markJump.updateStatusBar(); 106 | this.treeProvider.refresh(); 107 | }, this, subscriptions); 108 | vscode.workspace.onDidChangeConfiguration(() => { 109 | this.markJump.updateStatusBar(); 110 | this.treeProvider.refresh(); 111 | }, this, subscriptions); 112 | vscode.window.onDidChangeTextEditorViewColumn(event => { 113 | this.lastLine = undefined; 114 | this.markJump.updateStatusBar(); 115 | this.treeProvider.refresh(); 116 | }, this, subscriptions); 117 | vscode.window.onDidChangeActiveTextEditor(editor => { 118 | this.lastLine = undefined; 119 | this.markJump.updateStatusBar(); 120 | this.treeProvider.refresh(); 121 | }, this, subscriptions); 122 | vscode.window.onDidChangeTextEditorSelection(event => { 123 | if(event.selections.length > 1){ 124 | return; 125 | } 126 | if(this.lastLine === event.selections[0].active.line){ 127 | return; 128 | } 129 | this.lastLine = event.selections[0].active.line; 130 | this.markJump.updateStatusBar(); 131 | this.treeProvider.refresh(); 132 | }, this, subscriptions); 133 | 134 | this.disposable = vscode.Disposable.from(...subscriptions); 135 | } 136 | 137 | dispose(){ 138 | this.disposable.dispose(); 139 | } 140 | } 141 | 142 | class MarkJumpTreeProvider implements vscode.TreeDataProvider { 143 | private markJump: MarkJump; 144 | private context: vscode.ExtensionContext; 145 | private _onDidChangeTreeData = new vscode.EventEmitter(); 146 | readonly onDidChangeTreeData = this._onDidChangeTreeData.event; 147 | 148 | constructor(markJump: MarkJump, context: vscode.ExtensionContext){ 149 | this.markJump = markJump; 150 | this.context = context; 151 | } 152 | 153 | refresh() { 154 | this._onDidChangeTreeData.fire(); 155 | } 156 | 157 | getTreeItem(element: vscode.TreeItem){ 158 | return element; 159 | } 160 | 161 | getChildren(element?: vscode.TreeItem) { 162 | if (!this.markJump.treeAllow) { 163 | return []; 164 | } 165 | return this.markJump.getMarks( 166 | vscode.window.activeTextEditor, undefined, true 167 | ).then(marks => { 168 | return marks.map(mark => { 169 | let type = ""; 170 | if (mark.type === "note") { 171 | type = "book"; 172 | } else if (mark.type === "todo") { 173 | type = "pencil"; 174 | } else if (mark.type === "section") { 175 | type = "list-unordered"; 176 | } 177 | return { 178 | command: { 179 | title: "Reveal", 180 | command: "markJump.revealMark", 181 | arguments: [mark] 182 | }, 183 | label: `${ mark.description }`, 184 | iconPath: { 185 | light: this.context.asAbsolutePath( 186 | `./images/octicons/light/${ type }.svg` 187 | ), 188 | dark: this.context.asAbsolutePath( 189 | `./images/octicons/dark/${ type }.svg` 190 | ) 191 | } 192 | } 193 | }); 194 | }); 195 | } 196 | } 197 | 198 | interface BaseMarkItem { 199 | range: vscode.Range; 200 | uri: vscode.Uri; 201 | } 202 | 203 | interface MarkQuickPickItem extends vscode.QuickPickItem, BaseMarkItem {} 204 | 205 | interface MarkItem extends BaseMarkItem { 206 | type: "section" | "todo" | "note"; 207 | heading?: string; 208 | writer?: string; 209 | description: string; 210 | lineNumber: number; 211 | } 212 | 213 | interface MarkFilter { 214 | test(lineText: string): boolean; 215 | getItem( 216 | uri: vscode.Uri, 217 | lineNumber: number, 218 | lineText: string 219 | ): MarkItem | undefined; 220 | } 221 | 222 | class MarkJump { 223 | treeAllow = true; 224 | lastSelections: vscode.Selection[]; 225 | statusItem: vscode.StatusBarItem; 226 | useLimit = -1; 227 | testPatterns = []; 228 | private lastWarning: number = Date.now() - 10000; 229 | 230 | createStatusBar(){ 231 | if(this.statusItem){ 232 | return; 233 | } 234 | this.statusItem = vscode.window.createStatusBarItem( 235 | vscode.StatusBarAlignment.Left, 10 236 | ); 237 | this.statusItem.command = "markJump.jumpToMarks"; 238 | this.statusItem.hide(); 239 | this.updateStatusBar(); 240 | } 241 | 242 | dispose(){ 243 | this.statusItem.dispose(); 244 | } 245 | 246 | updateStatusBar(withProjectWide: boolean = true){ 247 | let configurations = vscode.workspace.getConfiguration("markJump"); 248 | this.treeAllow = configurations.get("showExplorerView"); 249 | if (!configurations.get("showStatusItem")) { 250 | this.statusItem.hide(); 251 | return; 252 | } 253 | 254 | let editor = vscode.window.activeTextEditor; 255 | let marks = this.getMarks( 256 | editor, undefined, withProjectWide 257 | ).then(marks => { 258 | if(marks.length <= 0){ 259 | this.statusItem.hide(); 260 | return; 261 | } 262 | 263 | let markCount = { 264 | section: 0, 265 | todo: 0, 266 | note: 0 267 | }; 268 | 269 | marks.forEach(mark => { 270 | markCount[mark.type] += 1; 271 | }); 272 | 273 | this.statusItem.text = `${ 274 | markCount.section > 0 ? 275 | `$(list-unordered) ${markCount.section} ` : "" 276 | }${ 277 | markCount.todo > 0 ? 278 | `$(pencil) ${markCount.todo} ` : "" 279 | }${ 280 | markCount.note > 0 ? 281 | `$(book) ${markCount.note}` : "" 282 | }`; 283 | 284 | let tooltips: string[] = []; 285 | if(markCount.section > 0){ 286 | tooltips.push( 287 | `${markCount.section} Section${ 288 | markCount.section > 1 ? "s" : "" 289 | }` 290 | ); 291 | } 292 | if(markCount.todo > 0){ 293 | tooltips.push( 294 | `${markCount.todo} TODO${ 295 | markCount.todo > 1 ? "s" : "" 296 | }` 297 | ); 298 | } 299 | if(markCount.note > 0){ 300 | tooltips.push( 301 | `${markCount.note} Note${ 302 | markCount.note > 1 ? "s" : "" 303 | }` 304 | ); 305 | } 306 | 307 | this.statusItem.tooltip = `${ 308 | editor ? "" : "In this project: " 309 | }${ 310 | tooltips.join(", ") 311 | }`; 312 | 313 | this.statusItem.show(); 314 | }); 315 | } 316 | 317 | jumpToPreviousMark(...filters: string[]){ 318 | let editor = vscode.window.activeTextEditor; 319 | if (!editor) { 320 | return; 321 | } 322 | this.getMarks(editor, { 323 | offset: editor.selection.active.line - 1, 324 | limit: -1 325 | }, false, ...filters).then(marks => { 326 | if (marks.length > 0) { 327 | return Promise.resolve(marks); 328 | } 329 | return this.getMarks(editor, { 330 | offset: editor.document.lineCount - 1, 331 | limit: -1 332 | }, false, ...filters); 333 | }).then(marks => { 334 | if (marks.length <= 0) { 335 | return; 336 | } 337 | this.revealMark(editor, marks[0]); 338 | }); 339 | } 340 | 341 | jumpToNextMark(...filters: string[]){ 342 | let editor = vscode.window.activeTextEditor; 343 | if (!editor) { 344 | return; 345 | } 346 | this.getMarks(editor, { 347 | offset: editor.selection.active.line + 1, 348 | limit: 1 349 | }, false, ...filters).then(marks => { 350 | if (marks.length > 0) { 351 | return Promise.resolve(marks); 352 | } 353 | return this.getMarks(editor, { 354 | offset: 0, 355 | limit: 1 356 | }, false, ...filters); 357 | }).then(marks => { 358 | if (marks.length <= 0) { 359 | return; 360 | } 361 | this.revealMark(editor, marks[0]); 362 | }); 363 | } 364 | 365 | jumpToMark(withProjectWide: boolean = true, ...filters: string[]){ 366 | this.jumpToEditorMark( 367 | vscode.window.activeTextEditor, withProjectWide, ...filters 368 | ); 369 | } 370 | 371 | buildHeading(length: number, headingSymbol: string){ 372 | if (length <= 0 || !headingSymbol) { 373 | return ""; 374 | } 375 | let firstSymbol = headingSymbol.substr(0, 1); 376 | let secondSymbol = headingSymbol.substr(1) || firstSymbol; 377 | let padding = " "; 378 | if (firstSymbol === " ") { 379 | padding = ""; 380 | } 381 | return `${firstSymbol}${ secondSymbol.repeat(length - 1) }${padding}`; 382 | } 383 | 384 | jumpToEditorMark( 385 | editor?: vscode.TextEditor, 386 | withProjectWide: boolean = true, 387 | ...filters: string[] 388 | ){ 389 | let configurations = vscode.workspace.getConfiguration("markJump"); 390 | this.getMarks(editor, undefined, withProjectWide, ...filters) 391 | .then(marks => { 392 | if(marks.length <= 0){ 393 | if(filters.length === 1 && filters.indexOf("todo") >= 0){ 394 | vscode.window.showInformationMessage( 395 | "No TODO left. Well done!" 396 | ); 397 | }else{ 398 | vscode.window.showInformationMessage("No mark is set."); 399 | } 400 | return; 401 | } 402 | 403 | let options: vscode.DecorationRenderOptions = { 404 | isWholeLine: true 405 | }; 406 | 407 | let darkValue = configurations.get( 408 | "highlightColor.dark" 409 | ); 410 | let lightValue = configurations.get( 411 | "highlightColor.light" 412 | ); 413 | 414 | if(darkValue){ 415 | options.dark = { 416 | backgroundColor: darkValue, 417 | overviewRulerColor: darkValue 418 | }; 419 | } 420 | if(lightValue){ 421 | options.light = { 422 | backgroundColor: lightValue, 423 | overviewRulerColor: lightValue 424 | }; 425 | } 426 | 427 | let headingSymbol = configurations.get("headingSymbol"); 428 | 429 | if(editor){ 430 | this.lastSelections = editor.selections; 431 | } 432 | let highlightDecoration = vscode.window.createTextEditorDecorationType( 433 | options 434 | ); 435 | let lastEditor: vscode.TextEditor = undefined; 436 | vscode.window.showQuickPick(marks.map(mark => { 437 | let item: MarkQuickPickItem = { 438 | range: mark.range, 439 | uri: mark.uri, 440 | label: "", 441 | description: undefined 442 | }; 443 | 444 | if(mark.type === "note"){ 445 | item.label = `${ 446 | this.buildHeading( 447 | (mark.heading || "").length, headingSymbol 448 | ) 449 | }$(book) NOTE: ${mark.description}` || ""; 450 | 451 | item.detail = ( 452 | mark.writer ? `by ${mark.writer}` : undefined 453 | ); 454 | 455 | item.description = `${ 456 | editor ? "" : `${path.basename(mark.uri.fsPath)} ` 457 | }on line ${mark.lineNumber + 1}`; 458 | }else if(mark.type === "todo"){ 459 | item.label = `${ 460 | this.buildHeading( 461 | (mark.heading || "").length, headingSymbol 462 | ) 463 | }$(pencil) TODO: ${mark.description}` || ""; 464 | 465 | item.detail = ( 466 | mark.writer ? `by ${mark.writer}` : undefined 467 | ); 468 | 469 | item.description = `${ 470 | editor ? "" : `${path.basename(mark.uri.fsPath)} ` 471 | }on line ${mark.lineNumber + 1}`; 472 | }else if(mark.type === "section"){ 473 | item.label = `${ 474 | this.buildHeading( 475 | (mark.heading || "").length, headingSymbol 476 | ) 477 | }$(list-unordered) ${mark.description}` || ""; 478 | item.description = `${ 479 | editor ? "" : `${path.basename(mark.uri.fsPath)} ` 480 | }on line ${mark.lineNumber + 1}`; 481 | } 482 | 483 | return item; 484 | }), { 485 | ignoreFocusOut: false, 486 | matchOnDescription: true, 487 | matchOnDetail: true, 488 | onDidSelectItem: (mark) => { 489 | if(!editor){ 490 | if(!configurations.get("alwaysOpenDocument")){ 491 | return; 492 | } 493 | vscode.workspace.openTextDocument( 494 | mark.uri 495 | ).then(document => { 496 | return vscode.window.showTextDocument( 497 | document, { 498 | preserveFocus: true, 499 | preview: true 500 | } 501 | ); 502 | }).then(editor => { 503 | if(lastEditor){ 504 | lastEditor.setDecorations( 505 | highlightDecoration, [] 506 | ); 507 | } 508 | editor.setDecorations( 509 | highlightDecoration, [mark.range] 510 | ); 511 | this.revealMark(editor, mark); 512 | lastEditor = editor; 513 | }); 514 | return; 515 | } 516 | editor.setDecorations(highlightDecoration, [mark.range]); 517 | this.revealMark(editor, mark); 518 | } 519 | }).then(mark => { 520 | if(lastEditor){ 521 | lastEditor.setDecorations( 522 | highlightDecoration, [] 523 | ); 524 | } 525 | if(!editor){ 526 | this.openAndRevealMark(mark); 527 | return; 528 | } 529 | 530 | this.revealMark(editor, mark); 531 | editor.setDecorations(highlightDecoration, []); 532 | highlightDecoration.dispose(); 533 | }); 534 | }); 535 | } 536 | 537 | openAndRevealMark(mark: BaseMarkItem){ 538 | let editor = vscode.window.visibleTextEditors.find((editor) => ( 539 | editor.document.uri.toString() === mark.uri.toString() 540 | )); 541 | ( 542 | editor ? 543 | Promise.resolve(editor.document) : 544 | vscode.workspace.openTextDocument(mark.uri) 545 | ).then((document) => vscode.window.showTextDocument(document, { 546 | preview: false 547 | })).then((editor) => this.revealMark(editor, mark)); 548 | } 549 | 550 | revealMark( 551 | editor: vscode.TextEditor, 552 | mark?: BaseMarkItem 553 | ){ 554 | if(!mark){ 555 | editor.revealRange( 556 | this.lastSelections[0], 557 | vscode.TextEditorRevealType.InCenterIfOutsideViewport 558 | ); 559 | editor.selections = this.lastSelections; 560 | return; 561 | } 562 | editor.revealRange( 563 | mark.range, vscode.TextEditorRevealType.InCenterIfOutsideViewport 564 | ); 565 | editor.selection = new vscode.Selection( 566 | mark.range.end, mark.range.end 567 | ); 568 | } 569 | 570 | getMarks( 571 | editor?: vscode.TextEditor, 572 | options?: { 573 | offset: number; 574 | limit: number; 575 | }, 576 | withProjectWide: boolean = true, 577 | ...filterKeys: string[] 578 | ){ 579 | return new Promise((resolve, reject) => { 580 | let configurations = vscode.workspace.getConfiguration("markJump"); 581 | let filters: MarkFilter[] = []; 582 | 583 | if(filterKeys.length <= 0 || filterKeys.indexOf("section") >= 0){ 584 | let patterns = configurations.get("sectionPatterns").concat( 585 | configurations.get("additionalSectionPatterns") 586 | ); 587 | filters.push(new SectionFilter(patterns)); 588 | } 589 | if(filterKeys.length <= 0 || filterKeys.indexOf("todo") >= 0){ 590 | let patterns = configurations.get("todoPatterns").concat( 591 | configurations.get("additionalTODOPatterns") 592 | ); 593 | filters.push(new TODOFilter(patterns)); 594 | } 595 | if(filterKeys.length <= 0 || filterKeys.indexOf("note") >= 0){ 596 | let patterns = configurations.get("notePatterns").concat( 597 | configurations.get("additionalNotePatterns") 598 | ); 599 | filters.push(new NoteFilter(patterns)); 600 | } 601 | if(!filters || filters.length <= 0){ 602 | console.log("[Mark Jump] No filter available"); 603 | return []; 604 | } 605 | 606 | if(editor){ 607 | return this.getEditorMarks(editor, options, ...filters).then( 608 | resolve.bind(this) 609 | ); 610 | }else if( 611 | withProjectWide && 612 | configurations.get("showProjectMarks") 613 | ){ 614 | return this.getWorkspaceMarks(...filters).then( 615 | resolve.bind(this) 616 | ); 617 | } 618 | 619 | resolve([]); 620 | }); 621 | } 622 | 623 | getEditorMarks( 624 | editor: vscode.TextEditor, 625 | options?: { 626 | offset: number; 627 | limit: number; 628 | }, 629 | ...filters: MarkFilter[] 630 | ){ 631 | return new Promise((resolve, reject) => { 632 | let items: MarkItem[] = []; 633 | let lineCount = editor.document.lineCount; 634 | 635 | let limit: number | undefined = options ? options.limit : undefined; 636 | let direction = limit === undefined || limit > 0 ? "down" : "up"; 637 | let lineNumber = options ? options.offset : ( 638 | direction === "down" ? 0 : lineCount - 1 639 | ); 640 | while (lineNumber >= 0 && lineNumber < lineCount) { 641 | let lineText = editor.document.lineAt(lineNumber).text; 642 | let filter = filters.find( 643 | filter => filter.test(lineText) 644 | ); 645 | if(!filter){ 646 | if (direction === "down") { 647 | lineNumber += 1; 648 | } else { 649 | lineNumber -= 1; 650 | } 651 | continue; 652 | } 653 | let item = filter.getItem( 654 | editor.document.uri, lineNumber, lineText 655 | ); 656 | if(!item){ 657 | if (direction === "down") { 658 | lineNumber += 1; 659 | } else { 660 | lineNumber -= 1; 661 | } 662 | continue; 663 | } 664 | items.push(item); 665 | 666 | if (direction === "down") { 667 | lineNumber += 1; 668 | } else { 669 | lineNumber -= 1; 670 | } 671 | } 672 | resolve(items); 673 | }); 674 | } 675 | 676 | getContentMarks(uri: vscode.Uri, ...filters: MarkFilter[]){ 677 | let configurations = vscode.workspace.getConfiguration("markJump"); 678 | let patterns = configurations.get("testPatterns").concat( 679 | configurations.get("additionalTestPatterns") 680 | ); 681 | 682 | let items: MarkItem[] = []; 683 | let data = fs.readFileSync(uri.fsPath); 684 | let content = data.toString(); 685 | 686 | let result = patterns.some( 687 | pattern => XRegExp.test(content, XRegExp(pattern)) 688 | ); 689 | 690 | if (!result) { 691 | return []; 692 | } 693 | 694 | let lines = content.split("\n"); 695 | 696 | lines.forEach((lineText, lineNumber) => { 697 | let filter = filters.find( 698 | filter => filter.test(lineText) 699 | ); 700 | if(!filter){ 701 | return; 702 | } 703 | let item = filter.getItem( 704 | uri, lineNumber, lineText 705 | ); 706 | if(!item){ 707 | return; 708 | } 709 | items.push(item); 710 | }); 711 | 712 | return items; 713 | } 714 | 715 | getWorkspaceMarks(...filters: MarkFilter[]){ 716 | return new Promise((resolve, reject) => { 717 | let configurations = vscode.workspace.getConfiguration("markJump"); 718 | 719 | vscode.workspace.findFiles( 720 | configurations.get("includeFilePattern"), 721 | configurations.get("excludeFilePattern") 722 | ).then(urls => { 723 | if (!urls) { 724 | return Promise.resolve([] as vscode.Uri[]); 725 | } 726 | let limit = configurations.get("maximumLimit"); 727 | let strictLimit = configurations.get("strictLimit"); 728 | 729 | if (limit < 0 || urls.length <= limit) { 730 | return Promise.resolve(urls); 731 | } 732 | 733 | if (strictLimit === "limit" || this.useLimit > 0) { 734 | return Promise.resolve(urls.slice(0, limit)); 735 | } else if (strictLimit === "disable" || this.useLimit === 0) { 736 | return Promise.resolve([] as vscode.Uri[]); 737 | } 738 | 739 | if (Date.now() - this.lastWarning < 5000) { 740 | return Promise.resolve([] as vscode.Uri[]); 741 | } 742 | 743 | return vscode.window.showWarningMessage( 744 | `Mark Jump is going to run through ${ 745 | urls.length 746 | } files, but the limit has set to ${ 747 | limit 748 | }.`, 749 | { 750 | title: `Use ${ limit } for now`, 751 | action: () => limit, 752 | isCloseAffordance: true 753 | }, { 754 | title: `Set to ${ urls.length }`, 755 | action: () => configurations.update( 756 | "maximumLimit", 757 | urls.length, 758 | vscode.ConfigurationTarget.Global 759 | ), 760 | isCloseAffordance: true 761 | }, { 762 | title: `Set to unlimited`, 763 | danger: { 764 | title: "Increase the limit to unlimited? This could impact the editor's performance a lot.", 765 | action: "Yes, increase to unlimited", 766 | cancel: "Cancel" 767 | }, 768 | action: () => configurations.update( 769 | "maximumLimit", 770 | -1, 771 | vscode.ConfigurationTarget.Global 772 | ), 773 | isCloseAffordance: true 774 | }, { 775 | title: `Disable for now`, 776 | action: () => 0, 777 | isCloseAffordance: true 778 | }, { 779 | title: `Close`, 780 | isCloseAffordance: true 781 | } 782 | ).then((action) => { 783 | if (!action) { 784 | this.lastWarning = Date.now(); 785 | return Promise.resolve([] as vscode.Uri[]); 786 | } 787 | 788 | if (!action.danger) { 789 | this.lastWarning = Date.now(); 790 | if (action.action) { 791 | let result = action.action(); 792 | if (typeof(result) === "number") { 793 | this.useLimit = result; 794 | return Promise.resolve(urls.slice(0, result)); 795 | } else { 796 | return result.then( 797 | () => Promise.resolve(urls) 798 | ); 799 | } 800 | } else { 801 | return Promise.resolve([] as vscode.Uri[]); 802 | } 803 | } 804 | let danger = action.danger; 805 | return vscode.window.showWarningMessage( 806 | danger.title, { 807 | title: danger.action, 808 | action: action.action, 809 | isCloseAffordance: true 810 | }, { 811 | title: danger.cancel, 812 | isCloseAffordance: true 813 | } 814 | ).then((action) => { 815 | this.lastWarning = Date.now(); 816 | if (!action) { 817 | return Promise.resolve([] as vscode.Uri[]); 818 | } 819 | if (action.action) { 820 | let result = action.action(); 821 | if (typeof(result) === "number") { 822 | this.useLimit = result; 823 | return Promise.resolve(urls.slice(0, result)); 824 | } else { 825 | return result.then( 826 | () => Promise.resolve(urls) 827 | ); 828 | } 829 | } else { 830 | return Promise.resolve([] as vscode.Uri[]); 831 | } 832 | }); 833 | }); 834 | }).then((urls) => { 835 | if (urls.length === 0) { 836 | return resolve([]); 837 | } 838 | let items: MarkItem[] = []; 839 | urls.forEach(url => { 840 | try{ 841 | items = items.concat( 842 | this.getContentMarks(url, ...filters) 843 | ); 844 | }catch(error){ 845 | return; 846 | } 847 | }); 848 | resolve(items); 849 | }) 850 | }); 851 | } 852 | } 853 | 854 | class SectionFilter implements MarkFilter { 855 | patterns: string[]; 856 | 857 | constructor(patterns: string[] = []){ 858 | this.patterns = patterns; 859 | } 860 | 861 | test(lineText: string): boolean { 862 | return this.patterns.some(pattern => { 863 | return XRegExp.test(lineText, XRegExp(pattern)); 864 | }); 865 | } 866 | 867 | getItem( 868 | uri: vscode.Uri, lineNumber: number, lineText: string 869 | ): MarkItem | undefined { 870 | let item: MarkItem | undefined = undefined; 871 | this.patterns.forEach(pattern => { 872 | let matches = XRegExp.exec(lineText, XRegExp(pattern)); 873 | if(!matches){ 874 | return; 875 | } 876 | item = { 877 | uri: uri, 878 | type: "section", 879 | range: new vscode.Range( 880 | lineNumber, 0, lineNumber, lineText.length 881 | ), 882 | heading: matches["heading"], 883 | description: matches["description"], 884 | lineNumber: lineNumber 885 | }; 886 | }); 887 | return item; 888 | } 889 | } 890 | 891 | class TODOFilter implements MarkFilter { 892 | patterns: string[]; 893 | 894 | constructor(patterns: string[] = []){ 895 | this.patterns = patterns; 896 | } 897 | 898 | test(lineText: string): boolean { 899 | return this.patterns.some(pattern => { 900 | return XRegExp.test(lineText, XRegExp(pattern)); 901 | }); 902 | } 903 | 904 | getItem( 905 | uri: vscode.Uri, lineNumber: number, lineText: string 906 | ): MarkItem | undefined { 907 | let item: MarkItem | undefined = undefined; 908 | this.patterns.forEach(pattern => { 909 | let matches = XRegExp.exec(lineText, XRegExp(pattern)); 910 | if(!matches){ 911 | return; 912 | } 913 | item = { 914 | uri: uri, 915 | type: "todo", 916 | range: new vscode.Range( 917 | lineNumber, 0, lineNumber, lineText.length 918 | ), 919 | heading: matches["heading"], 920 | description: matches["description"], 921 | writer: matches["writer"], 922 | lineNumber: lineNumber 923 | }; 924 | }); 925 | return item; 926 | } 927 | } 928 | 929 | class NoteFilter implements MarkFilter { 930 | patterns: string[]; 931 | 932 | constructor(patterns: string[] = []){ 933 | this.patterns = patterns; 934 | } 935 | 936 | test(lineText: string): boolean { 937 | return this.patterns.some(pattern => { 938 | return XRegExp.test(lineText, XRegExp(pattern)); 939 | }); 940 | } 941 | 942 | getItem( 943 | uri: vscode.Uri, lineNumber: number, lineText: string 944 | ): MarkItem | undefined { 945 | let item: MarkItem | undefined = undefined; 946 | this.patterns.forEach(pattern => { 947 | let matches = XRegExp.exec(lineText, XRegExp(pattern)); 948 | if(!matches){ 949 | return; 950 | } 951 | item = { 952 | uri: uri, 953 | type: "note", 954 | range: new vscode.Range( 955 | lineNumber, 0, lineNumber, lineText.length 956 | ), 957 | heading: matches["heading"], 958 | description: matches["description"], 959 | writer: matches["writer"], 960 | lineNumber: lineNumber 961 | }; 962 | }); 963 | return item; 964 | } 965 | } 966 | --------------------------------------------------------------------------------