├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── README.md ├── package.json ├── screens ├── backticks.gif ├── brackets.gif ├── doublequotes.gif ├── singlequotes.gif └── tags.gif ├── src └── extension.ts ├── tsconfig.json └── vsc-extension-quickstart.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules -------------------------------------------------------------------------------- /.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 | "outFiles": ["out/src"], 14 | "preLaunchTask": "npm" 15 | }] 16 | } -------------------------------------------------------------------------------- /.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/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 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vscode-quick-select 2 | 3 | 4 | Yes I know about the ⌃⇧⌘← and ⌃⇧⌘→ expand/shrink selection. Having come from VIM I think these are still missing. 5 | 6 | It now supports multilines automatic selection, matching correctly. 7 | 8 | **NEW:** you can also now toggle single/double quotes 9 | 10 | See the examples below. 11 | 12 | 13 | ## Installation 14 | 15 | Press F1 and narrow down the list commands by typing `extension`. Pick `Extensions: Install Extension`. 16 | Select the `Quick and Simple Text Selection` extension from the list 17 | 18 | 19 | ## Manual Install 20 | 21 | **Mac & Linux** 22 | ```sh 23 | cd $HOME/.vscode/extensions 24 | ``` 25 | **Windows** 26 | ```sh 27 | cd %USERPROFILE%\.vscode\extensions 28 | ``` 29 | 30 | **All Platforms** 31 | ``` 32 | git clone https://github.com/dbankier/vscode-quick-select.git 33 | cd vscode-quick-select 34 | npm install 35 | ``` 36 | 37 | 38 | ## Usage 39 | 40 | Here some examples - and it supports multiple selections. 41 | 42 | In the examples below use CTRL instead of for Windows. 43 | 44 | k " 45 | 46 | ![doublequotes](https://github.com/dbankier/vscode-quick-select/raw/master/screens/doublequotes.gif) 47 | 48 | k ' 49 | 50 | ![singlequotes](https://github.com/dbankier/vscode-quick-select/raw/master/screens/singlequotes.gif) 51 | 52 | **NEW:** You can also use this following shortcut to select either single, double quotes or backticks 53 | 54 | k ; 55 | 56 | **NEW:** You can also use this following shortcut to toggle quotes, e.g. `"word"` to `'word'` 57 | 58 | k : 59 | 60 | **NOTE:** the extensions can be configured to exclude backticks from selection or switching 61 | 62 | k ` 63 | 64 | ![singlequotes](https://github.com/dbankier/vscode-quick-select/raw/master/screens/backticks.gif) 65 | 66 | k ( and 67 | k [ and 68 | k { 69 | 70 | Using the following performs and outer selection: 71 | 72 | k ) and 73 | k ] and 74 | k } 75 | 76 | Or if you have already made in inner selection, use the same key combination again to expand to an outer selection. 77 | 78 | ![brackets](https://github.com/dbankier/vscode-quick-select/raw/master/screens/brackets.gif) 79 | 80 | 81 | k < 82 | 83 | This also selects the matching tag. 84 | 85 | k > 86 | 87 | This matches the tag value. 88 | 89 | ![brackets](https://github.com/dbankier/vscode-quick-select/raw/master/screens/tags.gif) 90 | 91 | ### Customisation 92 | 93 | ~~~ 94 | extension.selectSingleQuote 95 | extension.selectDoubleQuote 96 | extension.selectEitherQuote 97 | extension.switchQuotes 98 | extension.selectParenthesis 99 | extension.selectBackTick 100 | extension.selectSquareBrackets 101 | extension.selectCurlyBrackets 102 | extension.selectParenthesisOuter 103 | extension.selectSquareBracketsOuter 104 | extension.selectCurlyBracketsOuter 105 | extension.selectAngleBrackets 106 | extension.selectInTag 107 | ~~~ 108 | 109 | ## License 110 | 111 | MIT © [David Bankier @dbankier](https://github.com/dbankier) 112 | 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-quick-select", 3 | "displayName": "Quick and Simple Text Selection", 4 | "description": "Jump to select between quote, brackets, tags, etc", 5 | "version": "0.2.9", 6 | "publisher": "dbankier", 7 | "engines": { 8 | "vscode": "^1.0.3" 9 | }, 10 | "categories": [ 11 | "Other" 12 | ], 13 | "activationEvents": [ 14 | "onCommand:extension.selectSingleQuote", 15 | "onCommand:extension.selectDoubleQuote", 16 | "onCommand:extension.selectEitherQuote", 17 | "onCommand:extension.switchQuotes", 18 | "onCommand:extension.selectParenthesis", 19 | "onCommand:extension.selectBackTick", 20 | "onCommand:extension.selectSquareBrackets", 21 | "onCommand:extension.selectCurlyBrackets", 22 | "onCommand:extension.selectParenthesisOuter", 23 | "onCommand:extension.selectSquareBracketsOuter", 24 | "onCommand:extension.selectCurlyBracketsOuter", 25 | "onCommand:extension.selectAngleBrackets", 26 | "onCommand:extension.selectInTag" 27 | ], 28 | "main": "./out/src/extension", 29 | "contributes": { 30 | "commands": [ 31 | { 32 | "command": "extension.selectSingleQuote", 33 | "title": "Quick Select: Select inside single quote" 34 | }, 35 | { 36 | "command": "extension.selectDoubleQuote", 37 | "title": "Quick Select: Select inside double quote" 38 | }, 39 | { 40 | "command": "extension.selectEitherQuote", 41 | "title": "Quick Select: Select inside either quote" 42 | }, 43 | { 44 | "command": "extension.switchQuotes", 45 | "title": "Quick Select: Switch quotes" 46 | }, 47 | { 48 | "command": "extension.selectParenthesis", 49 | "title": "Quick Select: Select inside parenthesis" 50 | }, 51 | { 52 | "command": "extension.selectBackTick", 53 | "title": "Quick Select: Select inside back ticks" 54 | }, 55 | { 56 | "command": "extension.selectSquareBrackets", 57 | "title": "Quick Select: Select inside square brackets" 58 | }, 59 | { 60 | "command": "extension.selectCurlyBrackets", 61 | "title": "Quick Select: Select inside curly brackets" 62 | }, 63 | { 64 | "command": "extension.selectParenthesisOuter", 65 | "title": "Quick Select: Select outside parenthesis" 66 | }, 67 | { 68 | "command": "extension.selectSquareBracketsOuter", 69 | "title": "Quick Select: Select outside square brackets" 70 | }, 71 | { 72 | "command": "extension.selectCurlyBracketsOuter", 73 | "title": "Quick Select: Select outside curly brackets" 74 | }, 75 | { 76 | "command": "extension.selectAngleBrackets", 77 | "title": "Quick Select: Select inside angled brackets" 78 | }, 79 | { 80 | "command": "extension.selectInTag", 81 | "title": "Quick Select: Select inside tag" 82 | } 83 | ], 84 | "keybindings": [ 85 | { 86 | "command": "extension.selectSingleQuote", 87 | "key": "ctrl+k '", 88 | "mac": "cmd+k '", 89 | "when": "editorFocus" 90 | }, 91 | { 92 | "command": "extension.selectDoubleQuote", 93 | "key": "ctrl+k shift+'", 94 | "mac": "cmd+k shift+'", 95 | "when": "editorFocus" 96 | }, 97 | { 98 | "command": "extension.selectEitherQuote", 99 | "key": "ctrl+k ;", 100 | "mac": "cmd+k ;", 101 | "when": "editorFocus" 102 | }, 103 | { 104 | "command": "extension.switchQuotes", 105 | "key": "ctrl+k shift+;", 106 | "mac": "cmd+k shift+;", 107 | "when": "editorFocus" 108 | }, 109 | { 110 | "command": "extension.selectParenthesis", 111 | "key": "ctrl+k shift+9", 112 | "mac": "cmd+k shift+9", 113 | "when": "editorFocus" 114 | }, 115 | { 116 | "command": "extension.selectBackTick", 117 | "key": "ctrl+k `", 118 | "mac": "cmd+k `", 119 | "when": "editorFocus" 120 | }, 121 | { 122 | "command": "extension.selectSquareBrackets", 123 | "key": "ctrl+k [", 124 | "mac": "cmd+k [", 125 | "when": "editorFocus" 126 | }, 127 | { 128 | "command": "extension.selectCurlyBrackets", 129 | "key": "ctrl+k shift+[", 130 | "mac": "cmd+k shift+[", 131 | "when": "editorFocus" 132 | }, 133 | { 134 | "command": "extension.selectParenthesisOuter", 135 | "key": "ctrl+k shift+0", 136 | "mac": "cmd+k shift+0", 137 | "when": "editorFocus" 138 | }, 139 | { 140 | "command": "extension.selectSquareBracketsOuter", 141 | "key": "ctrl+k ]", 142 | "mac": "cmd+k ]", 143 | "when": "editorFocus" 144 | }, 145 | { 146 | "command": "extension.selectCurlyBracketsOuter", 147 | "key": "ctrl+k shift+]", 148 | "mac": "cmd+k shift+]", 149 | "when": "editorFocus" 150 | }, 151 | { 152 | "command": "extension.selectAngleBrackets", 153 | "key": "ctrl+k shift+,", 154 | "mac": "cmd+k shift+,", 155 | "when": "editorFocus" 156 | }, 157 | { 158 | "command": "extension.selectInTag", 159 | "key": "ctrl+k shift+.", 160 | "mac": "cmd+k shift+.", 161 | "when": "editorFocus" 162 | } 163 | ], 164 | "configuration": { 165 | "type": "object", 166 | "title": "Quick Select configuration", 167 | "properties": { 168 | "quick-select.includeBackticks": { 169 | "type": "boolean", 170 | "default": true, 171 | "description": "Include backticks when selecting or switching any quotes" 172 | } 173 | } 174 | } 175 | }, 176 | "scripts": { 177 | "vscode:prepublish": "tsc -p ./", 178 | "compile": "tsc -watch -p ./", 179 | "postinstall": "node ./node_modules/vscode/bin/install" 180 | }, 181 | "devDependencies": { 182 | "typescript": "^2.0.3", 183 | "vscode": "^1.0.0", 184 | "@types/node": "^6.0.40" 185 | }, 186 | "repository": { 187 | "type": "git", 188 | "url": "https://github.com/dbankier/vscode-quick-select/" 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /screens/backticks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbankier/vscode-quick-select/1c8aaa582381cc9efab558d901ef659afaba3c65/screens/backticks.gif -------------------------------------------------------------------------------- /screens/brackets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbankier/vscode-quick-select/1c8aaa582381cc9efab558d901ef659afaba3c65/screens/brackets.gif -------------------------------------------------------------------------------- /screens/doublequotes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbankier/vscode-quick-select/1c8aaa582381cc9efab558d901ef659afaba3c65/screens/doublequotes.gif -------------------------------------------------------------------------------- /screens/singlequotes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbankier/vscode-quick-select/1c8aaa582381cc9efab558d901ef659afaba3c65/screens/singlequotes.gif -------------------------------------------------------------------------------- /screens/tags.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbankier/vscode-quick-select/1c8aaa582381cc9efab558d901ef659afaba3c65/screens/tags.gif -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | import * as vscode from 'vscode'; 4 | 5 | // this method is called when your extension is activated 6 | // your extension is activated the very first time the command is executed 7 | export function activate(context: vscode.ExtensionContext) { 8 | var _ = undefined; 9 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectDoubleQuote', singleSelect.bind(_, { char: '"' }))); 10 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectSingleQuote', singleSelect.bind(_, { char: "'" }))); 11 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectEitherQuote', selectEitherQuote)); 12 | context.subscriptions.push(vscode.commands.registerCommand('extension.switchQuotes', switchQuotes)); 13 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectBackTick', singleSelect.bind(_, { char: "`", multiline: true }))); 14 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectParenthesis', matchingSelect.bind(_, { start_char: "(", end_char: ")" }))); 15 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectSquareBrackets', matchingSelect.bind(_, { start_char: "[", end_char: "]" }))); 16 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectCurlyBrackets', matchingSelect.bind(_, { start_char: "{", end_char: "}" }))); 17 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectParenthesisOuter', matchingSelect.bind(_, { start_char: "(", end_char: ")", outer: true }))); 18 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectSquareBracketsOuter', matchingSelect.bind(_, { start_char: "[", end_char: "]", outer: true }))); 19 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectCurlyBracketsOuter', matchingSelect.bind(_, { start_char: "{", end_char: "}", outer: true }))); 20 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectAngleBrackets', matchingSelect.bind(_, { start_char: "<", end_char: ">" }))); 21 | context.subscriptions.push(vscode.commands.registerCommand('extension.selectInTag', matchingSelect.bind(_, { start_char: ">", end_char: "<" }))); 22 | } 23 | 24 | // Replacables 25 | const starters = ['"', "'", "`", "(", "[", '{']; 26 | const enders = ['"', "'", "`", ")", "]", '}']; 27 | 28 | function findOccurances(doc: vscode.TextDocument, line: number, char: string): Array { 29 | var content = doc.lineAt(line); 30 | var matches = (content.text + "hack").split(char).reduce((acc, p) => { 31 | var len = p.length + 1; 32 | if (acc.length > 0) { 33 | len += acc[acc.length - 1]; 34 | } 35 | acc.push(len); 36 | return acc; 37 | }, []); 38 | matches.pop(); 39 | return matches; 40 | } 41 | 42 | function findNext(doc: vscode.TextDocument, line: number, char: string, start_index: number = 0, nest_char: string = undefined, nested: number = 0): vscode.Position { 43 | if (line === doc.lineCount) { return undefined }; 44 | var occurances = findOccurances(doc, line, char).filter(n => n >= start_index); 45 | var nests = nest_char ? findOccurances(doc, line, nest_char).filter(n => n >= start_index) : []; 46 | var occurance_index = 0; 47 | var nests_index = 0; 48 | while ((occurance_index < occurances.length || nests_index < nests.length) && nested >= 0) { 49 | if (occurances[occurance_index] < nests[nests_index] || !nests[nests_index]) { 50 | if (nested === 0) { 51 | return new vscode.Position(line, occurances[occurance_index]); 52 | } 53 | nested-- 54 | occurance_index++; 55 | } else if (nests[nests_index] < occurances[occurance_index] || !occurances[occurance_index]) { 56 | nested++; 57 | nests_index++; 58 | } 59 | } 60 | return findNext(doc, ++line, char, 0, nest_char, nested); 61 | } 62 | function findPrevious(doc: vscode.TextDocument, line: number, char: string, start_index?: number, nest_char: string = undefined, nested: number = 0): vscode.Position { 63 | if (line === -1) { return undefined }; 64 | if (start_index === undefined) { start_index = doc.lineAt(line).text.length; } 65 | var occurances = findOccurances(doc, line, char).filter(n => n <= start_index); 66 | var nests = nest_char ? findOccurances(doc, line, nest_char).filter(n => n <= start_index) : []; 67 | var occurance_index = occurances.length - 1; 68 | var nests_index = nests.length - 1; 69 | while ((occurance_index > -1 || nests_index > -1) && nested >= 0) { 70 | if (occurances[occurance_index] > nests[nests_index] || !nests[nests_index]) { 71 | if (nested === 0) { 72 | return new vscode.Position(line, occurances[occurance_index]); 73 | } 74 | nested-- 75 | occurance_index--; 76 | } else if (nests[nests_index] > occurances[occurance_index] || !occurances[occurance_index]) { 77 | nested++; 78 | nests_index--; 79 | } 80 | } 81 | return findPrevious(doc, --line, char, undefined, nest_char, nested); 82 | } 83 | 84 | function findSingleSelect(s: vscode.Selection, doc: vscode.TextDocument, char: string, outer?: boolean, multiline?: boolean) { 85 | let {line, character} = s.active; 86 | let matches = findOccurances(doc, line, char); 87 | let next = matches.find(a => a > character); 88 | let next_index = matches.indexOf(next); 89 | let offset = outer ? char.length : 0; 90 | if (matches.length > 1 && matches.length % 2 === 0) { 91 | // Jump inside the next matching pair 92 | if (next === -1) { return s } 93 | if (next_index % 2 !== 0) { 94 | next_index--; 95 | } 96 | //Automatically grow to outer selection 97 | if (!outer && 98 | new vscode.Position(line, matches[next_index]).isEqual(s.anchor) && 99 | new vscode.Position(line, matches[next_index + 1] - 1).isEqual(s.end)) { 100 | offset = char.length 101 | } 102 | return new vscode.Selection( 103 | new vscode.Position(line, matches[next_index] - offset), 104 | new vscode.Position(line, matches[next_index + 1] - 1 + offset) 105 | ); 106 | } else if (multiline) { 107 | let start_pos = findPrevious(doc, line, char, character) || new vscode.Position(line, matches[next_index]) 108 | if (!start_pos) { return s }; 109 | let end_pos: vscode.Position = findNext(doc, start_pos.line, char, start_pos.character + 1); 110 | //Automatically grow to outer selection 111 | if (!outer && 112 | start_pos.isEqual(s.anchor) && 113 | new vscode.Position(end_pos.line, end_pos.character - 1).isEqual(s.end)) { 114 | offset = char.length 115 | } 116 | if (start_pos && end_pos) { 117 | start_pos = new vscode.Position(start_pos.line, start_pos.character - offset); 118 | end_pos = new vscode.Position(end_pos.line, end_pos.character - 1 + offset); 119 | return new vscode.Selection(start_pos, end_pos) 120 | } 121 | } 122 | return s; 123 | 124 | } 125 | 126 | interface SingleSelectOptions { char: string; outer?: boolean, multiline?: boolean } 127 | function singleSelect({char, outer = false, multiline = false}: SingleSelectOptions) { 128 | let editor = vscode.window.activeTextEditor; 129 | if (!editor) { return; }; 130 | let doc = editor.document 131 | let sel = editor.selections 132 | editor.selections = sel.map(s => findSingleSelect(s, doc, char, outer, multiline)) 133 | } 134 | 135 | function getSwitchables() { 136 | const includeBackTicks = vscode.workspace.getConfiguration('quick-select').get('includeBackticks'); 137 | return ['"',"'"].concat(includeBackTicks ? ['`']:[]) 138 | } 139 | 140 | function selectEitherQuote() { 141 | const switchables = getSwitchables(); 142 | let editor = vscode.window.activeTextEditor; 143 | if (!editor) { return; }; 144 | let doc = editor.document 145 | let sel = editor.selections 146 | editor.selections = sel.map((s: vscode.Selection) => { 147 | let selections = switchables.map(char => findSingleSelect(s,doc,char, false, false)) 148 | .filter(sel => sel !== s) 149 | .filter(sel => sel.start.isBeforeOrEqual(s.start) && sel.end.isAfterOrEqual(s.end)) 150 | .sort((a,b) => a.start.isBefore(b.start) ? 1 : -1) 151 | if (selections.length > 0) { 152 | return selections[0] 153 | } 154 | return s; 155 | }) 156 | } 157 | 158 | function charRange(p: vscode.Position) { 159 | let end_pos = new vscode.Position(p.line, p.character + 1); 160 | return new vscode.Selection(p, end_pos) 161 | } 162 | function switchQuotes() { 163 | const switchables = getSwitchables(); 164 | let editor = vscode.window.activeTextEditor; 165 | let original_sel = editor.selections 166 | selectEitherQuote() 167 | if (!editor) { return; }; 168 | let doc = editor.document 169 | let sel = editor.selections 170 | sel.map((s: vscode.Selection) => { 171 | if (s.start.isEqual(s.end)) { return } 172 | //expand selection if needed 173 | let expand_start = switchables.indexOf(doc.getText(charRange(s.start))) === -1 ? 1 : 0 174 | let expand_end = switchables.indexOf(doc.getText(charRange(s.end))) === -1 ? 1 : 0 175 | let start_pos = new vscode.Position(s.start.line, s.start.character - expand_start); 176 | let end_pos = new vscode.Position(s.end.line, s.end.character - expand_end); 177 | s = new vscode.Selection(start_pos, end_pos) 178 | var char = doc.getText(charRange(s.start)) 179 | var edit = new vscode.WorkspaceEdit(); 180 | let next_index = switchables.indexOf(char)+1; 181 | if (next_index === switchables.length) { 182 | next_index = 0 183 | } 184 | let next_char = switchables[next_index]; 185 | edit.replace(doc.uri, charRange(s.start), next_char) 186 | edit.replace(doc.uri, charRange(s.end), next_char) 187 | vscode.workspace.applyEdit(edit) 188 | doc.getText() 189 | }) 190 | // restore orignal selection 191 | console.log(original_sel) 192 | editor.selections = original_sel; 193 | console.log(editor.selections) 194 | } 195 | interface MatchingSelectOptions { start_char: string, end_char: string, outer?: boolean } 196 | function matchingSelect({start_char, end_char, outer = false}: MatchingSelectOptions) { 197 | let editor = vscode.window.activeTextEditor; 198 | if (!editor) { return; }; 199 | let doc = editor.document 200 | let sel = editor.selections 201 | let success = false; 202 | let start_offset = outer ? start_char.length : 0; 203 | let end_offset = outer ? end_char.length : 0; 204 | editor.selections = sel.map(s => { 205 | let {line, character} = s.active; 206 | let starts = findOccurances(doc, line, start_char); 207 | let ends = findOccurances(doc, line, end_char); 208 | let start = starts.find(a => a > character); 209 | let end = ends.find(a => a > character); 210 | let start_index = starts.indexOf(start); 211 | let end_index = ends.indexOf(end); 212 | let start_pos: vscode.Position = findPrevious(doc, line, start_char, character, end_char) || new vscode.Position(line, starts[start_index]); 213 | if (!start_pos) { return s }; 214 | let end_pos: vscode.Position = findNext(doc, start_pos.line, end_char, start_pos.character + 1, start_char); 215 | if (start_pos && end_pos) { 216 | success = true; 217 | //Automatically grow to outer selection 218 | if (!outer && 219 | start_pos.isEqual(s.anchor) && 220 | new vscode.Position(end_pos.line, end_pos.character - 1).isEqual(s.end)) { 221 | start_offset = start_char.length; 222 | end_offset = end_char.length; 223 | } 224 | start_pos = new vscode.Position(start_pos.line, start_pos.character - start_offset); 225 | end_pos = new vscode.Position(end_pos.line, end_pos.character - 1 + end_offset); 226 | return new vscode.Selection(start_pos, end_pos) 227 | } 228 | return s; 229 | }) 230 | if (success && start_char === "<") { 231 | vscode.commands.executeCommand("editor.action.addSelectionToNextFindMatch") 232 | } 233 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": ["es6"], 7 | "sourceMap": true, 8 | "rootDir": "." 9 | }, 10 | "exclude": [ 11 | "node_modules", 12 | ".vscode-test" 13 | ] 14 | } -------------------------------------------------------------------------------- /vsc-extension-quickstart.md: -------------------------------------------------------------------------------- 1 | # Welcome to your first VS Code Extension 2 | 3 | ## What's in the folder 4 | * This folder contains all of the files necessary for your extension 5 | * `package.json` - this is the manifest file in which you declare your extension and command. 6 | The sample plugin registers a command and defines its title and command name. With this information 7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. 8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command. 9 | The file exports one function, `activate`, which is called the very first time your extension is 10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. 11 | We pass the function containing the implementation of the command as the second parameter to 12 | `registerCommand`. 13 | 14 | ## Get up and running straight away 15 | * press `F5` to open a new window with your extension loaded 16 | * run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World` 17 | * set breakpoints in your code inside `src/extension.ts` to debug your extension 18 | * find output from your extension in the debug console 19 | 20 | ## Make changes 21 | * you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts` 22 | * you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes 23 | 24 | ## Explore the API 25 | * you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts` 26 | 27 | ## Run tests 28 | * open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests` 29 | * press `F5` to run the tests in a new window with your extension loaded 30 | * see the output of the test result in the debug console 31 | * make changes to `test/extension.test.ts` or create new test files inside the `test` folder 32 | * by convention, the test runner will only consider files matching the name pattern `**.test.ts` 33 | * you can create folders inside the `test` folder to structure your tests any way you want --------------------------------------------------------------------------------