├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src └── extension.ts ├── tsconfig.json ├── vscode-complete-statement.png └── vscode-complete-statement.svg /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: pubilsh 2 | 3 | 4 | on: 5 | release: 6 | types: [created] 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - run: npm ci 17 | - name: Publish to Open VSX Registry 18 | uses: HaaLeo/publish-vscode-extension@v0 19 | with: 20 | pat: ${{ secrets.OPEN_VSX_TOKEN }} 21 | -------------------------------------------------------------------------------- /.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 | "outDir": "${workspaceRoot}/out/src", 14 | "preLaunchTask": "npm" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.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 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 0.4.0 4 | 5 | - Fix an issue that complete statement will move down one extra line when the cursor is at the end of the line. 6 | - Supports two new keywords: loop & fn (used in Rust). 7 | - Publish to open-vsx.org 8 | 9 | ## 0.3.0 10 | 11 | - Create a new line after completion and move the cursor down. 12 | 13 | ## 0.2.0 14 | 15 | - Support functions in more languages such as Swift, Kotlin, Go, Scala, C. 16 | - Support class, interface, object. 17 | - Insert a blank line on a line already ending with ';'. 18 | - Press `ctrl+;` again to move cursor to next line for complex structure. 19 | - Support Allman style. 20 | - Migrate vscode engine from 1.4 to 1.31 21 | 22 | ## 0.1.0 23 | 24 | - Migrate to TypeScript 2. 25 | - Target: es5 -> es6. 26 | - Complete JavaScript object notation. 27 | 28 | ## 0.0.2016 29 | 30 | Fix wrong urls in package.json. 31 | 32 | ## 0.0.0 33 | 34 | Initial release 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission to use, copy, modify, and/or distribute this software for any 2 | purpose with or without fee is hereby granted. 3 | 4 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 5 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 6 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 7 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 8 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 9 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 10 | PERFORMANCE OF THIS SOFTWARE. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Complete Statement with semicolon in vscode. 2 | 3 | Mimic IntelliJ's complete statement. 4 | In other words: 5 | 6 | - Complete normal statement with `;`, insert a newline and move down. 7 | - Try to complete complex structures with braces. 8 | 9 | Works with languages with a C style syntax. 10 | 11 | Status 12 | ------ 13 | 14 | This project is orphaned. 15 | I switched to Neovim as my mainly used editor. 16 | I still use vscode occasionally, 17 | but for languages not requiring a semicolon to end a statement. 18 | 19 | Pull requests are still welcome, though. 20 | And if you want to maintain this project, 21 | please open an issue or send a pull request, 22 | I will add you to the collaborators of the source code repository. 23 | 24 | Install 25 | ------- 26 | 27 | The version on marketplace is outdated. 28 | Please use [the version on open-vsx.org][open-vsx] ([vscodium] uses open-vsx by default). 29 | You can also download the vsix file at [GitHub releases page][releases], 30 | and manually install it via "vscode > Extensions > Install from VSIX...". 31 | 32 | [open-vsx]: https://open-vsx.org/extension/weakish/complete-statement 33 | [vscodium]: https://vscodium.com/ 34 | [releases]: https://github.com/weakish/vscode-complete-statement/releases 35 | 36 | If you want to try the cutting-edge version (`master`), 37 | you can clone this repository, and package it yourself: 38 | 39 | ```sh 40 | npx vsce package 41 | ``` 42 | 43 | Key binding 44 | ----------- 45 | 46 | This extension uses `ctrl+;` (`cmd+;` on mac) 47 | since vscode already uses `ctrl+shift+enter`. 48 | 49 | You can rebind `extension.complete-statement` to `ctrl+shift+enter`. 50 | 51 | BTW, `ctrl+;` is easier to remember and type than `ctrl+shift+enter`. 52 | I myself use `ctrl+enter` since `ctrl+;` is hard to type in dvorak. 53 | 54 | Example 55 | ------- 56 | 57 | We use `][` to represent cursor. 58 | 59 | ```typescript 60 | ][ 61 | let a_number = 2][ # decide to specify type 62 | let a_number: number][ = 2 63 | // press `ctrl+;` (`cmd+;` on mac) 64 | let a_number: number = 2; 65 | ][ 66 | let semicolon: string][ = "already exist"; 67 | // `ctrl+;` 68 | let semicolon: string = "already exist"; 69 | ][ 70 | function works_too(para: number][) 71 | // `ctrl+;` 72 | function works_too(para: number) { 73 | ][ 74 | } 75 | // Respects `tabSize` setting. If `tabSize` unset, use 4 spaces. 76 | function works_too(para: number) { 77 | if (a_number == 1][) 78 | } 79 | // `ctrl+;` 80 | function works_too(para: number) { 81 | if (a_number == 1) { 82 | ][ 83 | } 84 | } 85 | ``` 86 | 87 | The above example uses TypeScript, 88 | but this extension works in most languages with a C like style, 89 | such as JavaScript, Java, Ceylon, and C itself. 90 | This extension also works in languages like Kotlin, Scala, Swift, and so on. 91 | But I recommend you only use it to complete complete structures, 92 | not single statement since it will append a semicolon (`;`) at the end. 93 | 94 | Configuration 95 | ------------- 96 | 97 | By default, complete-statement uses Java style (beginning brace on same line). 98 | To use Allman style (beginning brace on its own line), 99 | add the following line in settings: 100 | 101 | ```json 102 | "complete-statement.allman": true 103 | ``` 104 | 105 | Bugs 106 | ---- 107 | 108 | - This extension does not understand semantics of programming languages. 109 | So complete structure may not work as you expected. 110 | 111 | For example, it cannot completes `if` with multiple line conditions. 112 | The "parsing" is *very naive*, only covering limited conditions. 113 | 114 | - Indented with tab is not supported yet. Pull request is welcome. 115 | 116 | License 117 | ------- 118 | 119 | 0BSD 120 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "complete-statement", 3 | "displayName": "Complete Statement", 4 | "description": "complete statement for languages with a syntax similar to C", 5 | "version": "0.4.0", 6 | "publisher": "weakish", 7 | "engines": { 8 | "vscode": "^1.45.1" 9 | }, 10 | "categories": [ 11 | "Other" 12 | ], 13 | "activationEvents": [ 14 | "onCommand:extension.complete-statement" 15 | ], 16 | "main": "./out/extension.js", 17 | "contributes": { 18 | "commands": [ 19 | { 20 | "command": "extension.complete-statement", 21 | "title": "Complete Statement" 22 | } 23 | ], 24 | "keybindings": [ 25 | { 26 | "key": "ctrl+;", 27 | "mac": "cmd+;", 28 | "when": "editorTextFocus", 29 | "command": "extension.complete-statement" 30 | } 31 | ], 32 | "configuration": { 33 | "type": "object", 34 | "title": "complete-statement", 35 | "properties": { 36 | "complete-statement": { 37 | "type": "boolean", 38 | "default": false, 39 | "description": "Allman style (beginning brace on its own line)." 40 | } 41 | } 42 | } 43 | }, 44 | "scripts": { 45 | "vscode:prepublish": "npm run compile", 46 | "compile": "tsc -p ./", 47 | "watch": "tsc -watch -p ./" 48 | }, 49 | "devDependencies": { 50 | "typescript": "^3.9.2", 51 | "@types/vscode": "^1.45.1", 52 | "@types/node": "^12.12.0" 53 | }, 54 | "license": "0BSD", 55 | "repository": { 56 | "type": "git", 57 | "url": "https://github.com/weakish/vscode-complete-statement.git" 58 | }, 59 | "bugs": { 60 | "url": "https://github.com/weakish/vscode-complete-statement/issues" 61 | }, 62 | "keywords": [ 63 | "completion", 64 | "semicolon", 65 | "c-syntax", 66 | "intelliJ" 67 | ], 68 | "icon": "vscode-complete-statement.png", 69 | "badges": [ 70 | { 71 | "description": "0BSD", 72 | "url": "https://img.shields.io/badge/license-0BSD-lightgrey.svg", 73 | "href": "https://opensource.org/licenses/FPL-1.0.0" 74 | }, 75 | { 76 | "description": "TypeScript", 77 | "url": "https://img.shields.io/badge/TypeScript-1.8.10-blue.svg", 78 | "href": "http://www.typescriptlang.org/" 79 | } 80 | ], 81 | "preview": false, 82 | "dependencies": {} 83 | } 84 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | 3 | 4 | export function activate(extensionContext: vscode.ExtensionContext) { 5 | 6 | console.log('"complete-statement" is activated.') 7 | 8 | const disposable: vscode.Disposable = 9 | vscode.commands.registerTextEditorCommand( 10 | 'extension.complete-statement', 11 | (textEditor, textEditorEdit) => 12 | { complete_statement(textEditor, textEditorEdit) } 13 | ) 14 | extensionContext.subscriptions.push(disposable) 15 | } 16 | export function deactivate() { 17 | console.log('"complete-statement" is deactivated.') 18 | } 19 | 20 | function complete_statement(textEditor: vscode.TextEditor, 21 | textEditorEdit: vscode.TextEditorEdit 22 | ): void 23 | { 24 | let current_line_number: number = textEditor.selection.start.line 25 | let current_line: vscode.TextLine = textEditor.document.lineAt(current_line_number) 26 | 27 | // Get indentation level here for use with either 28 | // new lines after semicolon or new block of code. 29 | // Assuming use spaces to indent. 30 | const tab_stop: number = vscode.workspace.getConfiguration('editor').get('tabSize', 4) 31 | let indent_level: number = 0 32 | if (current_line.text.startsWith(' ')) // indented 33 | { 34 | const indent_position: number = 35 | current_line.text.lastIndexOf(" ".repeat(tab_stop)) 36 | indent_level = indent_position / tab_stop + 1 37 | } 38 | const indent_space_count: number = tab_stop * (indent_level + 1) 39 | const indent_spaces: string = " ".repeat(indent_space_count) 40 | const less_indent_spaces: string = " ".repeat(tab_stop * indent_level) 41 | const is_at_end = (): boolean => { 42 | const editor = vscode.window.activeTextEditor 43 | if (editor) { 44 | let position: vscode.Position = editor.selection.active 45 | return position.character == current_line.range.end.character 46 | } else { 47 | return false 48 | } 49 | } 50 | 51 | if (current_line.text.trim() === '}') 52 | { 53 | vscode.commands.executeCommand('cursorMove', {'to': 'up'}) 54 | vscode.commands.executeCommand('cursorMove', {'to': 'wrappedLineEnd'}) 55 | } 56 | else if (looks_like_complex_structure(current_line)) 57 | { 58 | if (current_line.text.endsWith('{')) 59 | { 60 | vscode.commands.executeCommand('cursorMove', {'to': 'down'}) 61 | vscode.commands.executeCommand('cursorMove', {'to': 'wrappedLineEnd'}) 62 | } 63 | else 64 | { 65 | let braces: string 66 | const allman: boolean = 67 | vscode.workspace.getConfiguration('complete-statement').get('allman', false) 68 | if (allman) 69 | { 70 | braces = `\n${less_indent_spaces}{\n${indent_spaces}` + 71 | `\n${less_indent_spaces}}` 72 | textEditorEdit.insert(current_line.range.end, braces) 73 | } 74 | else 75 | { 76 | braces = `{\n${indent_spaces}\n${less_indent_spaces}}` 77 | if (current_line.text.endsWith(" ")) // avoid duplicated spaces 78 | { 79 | // pass 80 | } 81 | else 82 | { 83 | braces = ` ${braces}` 84 | } 85 | textEditorEdit.insert(current_line.range.end, braces) 86 | } 87 | 88 | // After completion, vscode will move the cursor to the end of the added text 89 | // if the cursor is currently at the end of the line, otherwise the cursor 90 | // stays on the current line. 91 | // Figure out is_at_end here. 92 | // Move the cursor into the newly created block. 93 | if (is_at_end()) { 94 | vscode.commands.executeCommand('cursorMove', {'to': 'up'}) 95 | } 96 | else { 97 | vscode.commands.executeCommand('cursorMove', {'to': 'down'}) 98 | } 99 | vscode.commands.executeCommand('cursorMove', {'to': 'wrappedLineEnd'}) 100 | } 101 | } 102 | else 103 | { 104 | if (current_line.text.trim() !== '' && !current_line.text.endsWith(';')) { 105 | textEditorEdit.insert(current_line.range.end, ';') 106 | } 107 | 108 | textEditorEdit.insert(current_line.range.end, '\n' + less_indent_spaces) 109 | // If the cursor is currently at the end of the line, 110 | // vscode will move it to the end of the next line after insertion. 111 | // Otherwise we will move the cursor ourselves. 112 | if(!is_at_end()) { 113 | vscode.commands.executeCommand('cursorMove', {'to': 'down'}) 114 | vscode.commands.executeCommand('cursorMove', {'to': 'wrappedLineEnd'}) 115 | } 116 | } 117 | } 118 | 119 | 120 | function looks_like_complex_structure(line: vscode.TextLine): boolean 121 | { 122 | const trimmed: string = line.text.trim() 123 | // class and object 124 | if (trimmed.startsWith('class ') || 125 | trimmed.startsWith('interface ') || 126 | trimmed.startsWith('object ')) 127 | { 128 | return true 129 | } 130 | // if else 131 | else if (trimmed.startsWith('if (') || 132 | trimmed.startsWith('if(') || 133 | trimmed.startsWith('} else') || 134 | trimmed.startsWith('else')) 135 | { 136 | return true 137 | } 138 | // switch 139 | else if (trimmed.startsWith('switch (') || 140 | trimmed.startsWith('switch(')) 141 | { 142 | return true 143 | } 144 | // loop 145 | else if (trimmed.startsWith('for (') || 146 | trimmed.startsWith('for(') || 147 | trimmed.startsWith('while (') || 148 | trimmed.startsWith('while(') || 149 | trimmed.startsWith('do') || 150 | trimmed.startsWith('loop')) 151 | { 152 | return true 153 | } 154 | // function 155 | else if ( 156 | trimmed.startsWith('function ') || // javascript 157 | trimmed.startsWith('func ') || // swift 158 | trimmed.startsWith('fun ') || // kotlin 159 | trimmed.startsWith('def ') || // scala 160 | trimmed.startsWith('fn ') || // rust 161 | // Regexp is expensive, so we test it after other structures. 162 | /^\w+\s\w+\s?\(/.test(trimmed)) // c, java, ceylon 163 | { 164 | return true 165 | } 166 | else 167 | { 168 | return false 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true, 12 | "noUnusedParameters": true, 13 | "noUnusedLocals": true, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": true 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /vscode-complete-statement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weakish/vscode-complete-statement/16db0a9fa8b7e9a54862a836a9b51a65edf6daca/vscode-complete-statement.png -------------------------------------------------------------------------------- /vscode-complete-statement.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | semicolon 20 | 22 | 40 | 47 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | semicolon 57 | 59 | 60 | 61 | Jakukyo Friel<weakish@gmail.com> 62 | 63 | 64 | https://weakish.github.com/vscode-complete-statement 65 | Icon for vscode extension "complete-statement 66 | 67 | 69 | 71 | 73 | 75 | 76 | 77 | 78 | 83 | 96 | 106 | 107 | 108 | --------------------------------------------------------------------------------