├── .gitignore ├── emacs_pacifica.png ├── .vscodeignore ├── tslint.json ├── test ├── utils.ts ├── index.ts └── c-k.test.ts ├── tsconfig.json ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── src ├── operation.ts ├── extension.ts └── editor.ts ├── CHANGELOG.md ├── README.md ├── LICENSE └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | out 3 | node_modules -------------------------------------------------------------------------------- /emacs_pacifica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SebastianZaha/vscode-emacs-friendly/HEAD/emacs_pacifica.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | test/** 5 | src/** 6 | **/*.map 7 | .gitignore 8 | tsconfig.json 9 | vsc-extension-quickstart.md 10 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-unused-expression": true, 4 | "no-duplicate-variable": true, 5 | "no-unused-variable": true, 6 | "curly": false, 7 | "class-name": true, 8 | "semicolon": ["never"], 9 | "triple-equals": false 10 | } 11 | } -------------------------------------------------------------------------------- /test/utils.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode' 2 | 3 | export function moveCursorToBeginning(): Thenable<{}> { 4 | vscode.commands.executeCommand("cursorMove", {to: "wrappedLineStart", select: false}) 5 | return vscode.commands.executeCommand("cursorMove", {to: "up", by: "line", select: false, value: Number.MAX_VALUE}) 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6", 8 | "esnext.asynciterable" 9 | ], 10 | "sourceMap": true, 11 | "rootDir": ".", 12 | "noUnusedLocals": true 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | ".vscode-test" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | // 2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 3 | // 4 | // This file is providing the test runner to use when running extension tests. 5 | // By default the test runner in use is Mocha based. 6 | // 7 | // You can provide your own test runner if you want to override it by exporting 8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 9 | // host can call to run the tests. The test runner is expected to use console.log 10 | // to report the results back to the caller. When the tests are finished, return 11 | // a possible error to the callback or null if none. 12 | 13 | var testRunner = require('vscode/lib/testrunner'); 14 | 15 | // You can directly control Mocha options by uncommenting the following lines 16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 17 | testRunner.configure({ 18 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 19 | useColors: true // colored output from test results 20 | }); 21 | 22 | module.exports = testRunner; -------------------------------------------------------------------------------- /.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 background mode 26 | "isBackground": true, 27 | 28 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 29 | "problemMatcher": "$tsc-watch" 30 | } -------------------------------------------------------------------------------- /.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": ["${workspaceRoot}/out/src/**/*.js"], 14 | "preLaunchTask": "npm" 15 | }, 16 | { 17 | "name": "Extension Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "runtimeExecutable": "${execPath}", 21 | "args": [ 22 | "--extensionDevelopmentPath=${workspaceRoot}", 23 | "--extensionTestsPath=${workspaceRoot}/out/test" ], 24 | "stopOnEntry": false, 25 | "sourceMaps": true, 26 | "outFiles": [ 27 | "${workspaceRoot}/out/src/**/*.js", 28 | "${workspaceRoot}/out/test/**/*.js"], 29 | "preLaunchTask": "npm" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /test/c-k.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert' 2 | import * as vscode from 'vscode' 3 | 4 | import * as utils from './utils' 5 | import {Editor} from '../src/editor' 6 | 7 | suite("C-K functionality", () => { 8 | 9 | test("EOL behavior", async () => { 10 | 11 | let c = "line 1\nline 2\nline 3\n", 12 | e = new Editor() 13 | 14 | let doc = await vscode.workspace.openTextDocument({content: c, language: 'text'}) 15 | await vscode.window.showTextDocument(doc) 16 | 17 | await e.kill() 18 | await e.kill() 19 | await e.kill() 20 | assert.equal(doc.getText(), "\nline 3\n") 21 | await e.yank() 22 | assert.equal(doc.getText(), c) 23 | 24 | await utils.moveCursorToBeginning() 25 | 26 | await e.kill() 27 | assert.equal(doc.getText(), "\nline 2\nline 3\n") 28 | await e.yank() 29 | assert.equal(doc.getText(), c) 30 | 31 | await utils.moveCursorToBeginning() 32 | 33 | await e.kill() 34 | await e.kill() 35 | assert.equal(doc.getText(), "line 2\nline 3\n") 36 | await e.yank() 37 | assert.equal(doc.getText(), c) 38 | 39 | await utils.moveCursorToBeginning() 40 | 41 | await e.kill() 42 | await e.kill() 43 | await e.kill() 44 | await e.kill() 45 | assert.equal(doc.getText(), "line 3\n") 46 | await e.yank() 47 | assert.equal(doc.getText(), c) 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /src/operation.ts: -------------------------------------------------------------------------------- 1 | import {Editor} from './editor'; 2 | 3 | export class Operation { 4 | private editor: Editor; 5 | private commandList: { [key: string]: (...args: any[]) => any, thisArgs?: any } = {}; 6 | 7 | constructor() { 8 | this.editor = new Editor(); 9 | this.commandList = { 10 | 'C-k': () => { 11 | this.editor.kill(); 12 | }, 13 | 'C-w': () => { 14 | this.editor.cut() 15 | }, 16 | 'M-w': () => { 17 | this.editor.copy() 18 | }, 19 | 'C-y': () => { 20 | this.editor.yank() 21 | }, 22 | "C-x_C-o": () => { 23 | this.editor.deleteBlankLines(); 24 | }, 25 | "C-/": () => { 26 | this.editor.undo(); 27 | this.editor.setStatusBarMessage("Undo!"); 28 | }, 29 | 'C-j': () => { 30 | this.editor.breakLine(); 31 | }, 32 | 'C-g': () => { 33 | this.editor.setStatusBarMessage("Quit"); 34 | }, 35 | "C-S_bs": () => { 36 | this.editor.deleteLine(); 37 | }, 38 | 'C-l': () => { 39 | this.editor.scrollLineToCenterTopBottom() 40 | } 41 | }; 42 | } 43 | 44 | getCommand(commandName: string): (...args: any[]) => any { 45 | return this.commandList[commandName]; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.9.0 (2019-05-06) 2 | - Fixed focus and movement keys interaction when find widget is open 3 | - Fixed movement keys in terminal 4 | - Bound Ctrl-o to insert line before 5 | - Fixed multi-cursor handling and Ctrl-g 6 | - Fixed remote development issues caused by clipboardy 7 | - Added C-_ as undo 8 | - Added C-x r as open recent (was C-r and overridden) 9 | - Fixed split window behavior to better resemble emacs 10 | 11 | ## 0.8.2 (2017-10-09) 12 | - Fix replaceOne shortcut to correctly only replace the currently selected match 13 | 14 | ## 0.8.1 (2017-10-09) 15 | - Do not close search widgtet on horizontal movement 16 | - Add shortcut for replace. 17 | - Add shortcuts for jump-to-next-error and jump-to-previous-error 18 | - Fix precondition for deleteWordLeft 19 | 20 | ## 0.8.0 (2017-08-11) 21 | - Removed register mode, it was introducing too much typing latency 22 | - Extend C-l to also position cursor at top / center / bottom of viewport 23 | - Fix C-f to work in integrated terminal 24 | 25 | ## 0.7.0 (2017-07-15) 26 | - Change C-j to behave as similar as possible as ENTER 27 | - Add C-m as a C-j alias (in early emacs versions C-m breaks the line, not C-j) 28 | - Add missing M-backspace shortcut to delete left word 29 | - Fix C-p and C-e shortcuts to work on the integrated terminal, instead of opening the command palette 30 | 31 | ## 0.6.1 (2017-06-27) 32 | - Fix cut to end of line operation when on the last line of a file 33 | 34 | ## 0.6.0 (2017-06-26) 35 | - Continuous kills from the same cursor position will augment clipboard 36 | - Killing removes end of line character when only whitespace is left on line 37 | - Bind toggle zen mode to C-x z, the default was conflicting 38 | - Close search widget on many cursor moves & other editor actions 39 | 40 | ## 0.5.0 (2017-03-25) 41 | - Added bindings to manipulate window splitting and joining 42 | 43 | ## 0.4.0 (2017-03-16) 44 | - Using clipboardy for system clipboard access, old dependency does not work on linux. 45 | 46 | ## 0.3.2 (2017-03-10) 47 | - Added icon 48 | 49 | ## 0.3.0 (2017-03-09) 50 | - Repackaged and republished for ease of access on vscode marketplace. 51 | 52 | ## 0.2.0 (2017-01-14) 53 | - Forked and merged various pull requests and fixes 54 | - The clipboard handling is simplified by the removal of the emacs-only kill ring (which was also an unfinished implementation in the original). Copy, Cut, Yank and C-K work with the system clipboard now. 55 | - C+x k to close tab, C+x C-k all tabs 56 | - C+l centers screen on the cursor line 57 | - C+x C+f bound to quick open file 58 | - yank overwrites selection 59 | 60 | ## 0.1.1 (2016-11-04) 61 | - Fix #24: C-g does not cancel suggestions and prompts 62 | - Update vscode engine 63 | 64 | ## 0.1.0 65 | - Fix: IntelliSense Suggestion. 66 | - Add C-x b. 67 | 68 | ## 0.0.10 (2016-08-29) 69 | - Fix #18: Add Esc to step out from mark mode. 70 | - Add C-x C-o. 71 | - Fix: do not enter automatically in Mark Mode after Yanking. 72 | 73 | ## 0.0.9 (2016-07-24) 74 | - Add Kill-ring. 75 | - Alt-x show commands palette. 76 | 77 | All changes written by gizak, sammy44nts. Thanks 78 | 79 | ## 0.0.7 (2016-07-14) 80 | - bug fix for C-k. **Thanks trezm.** 81 | - C-x C-f execute "workbench.action.files.openFile". 82 | 83 | Thanks sammy44nts. 84 | 85 | ## 0.0.6 86 | - C-p and C-n can be used in other panels such as Suggestion and Hint. 87 | - Fix bug C-x C-f won't open file explorer. 88 | - Add one more undo operation C-/ 89 | - Add redo operation C-x z 90 | - Fix incorrect column moving after using C-a and C-e 91 | 92 | These commands and bug fixes were coding by kpping. Thanks. :) 93 | 94 | ## 0.0.5 95 | - Change the processing of C-u, C-h. 96 | - Change the processing of C-x C-f, C-x C-w, C-x C-s. 97 | 98 | ## 0.0.4 99 | - Modify the search operation. 100 | 101 | ## 0.0.3 102 | - Fixed a bug that occurred when you start from the command line. 103 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import {Operation} from './operation'; 3 | 4 | var inMarkMode: boolean = false; 5 | var markHasMoved: boolean = false; 6 | export function activate(context: vscode.ExtensionContext): void { 7 | let op = new Operation(), 8 | commandList: string[] = [ 9 | "C-g", 10 | 11 | // Edit 12 | "C-k", "C-w", "M-w", "C-y", "C-x_C-o", 13 | "C-/", "C-j", "C-S_bs", 14 | 15 | // Navigation 16 | "C-l", 17 | ], 18 | cursorMoves: string[] = [ 19 | "cursorUp", "cursorDown", "cursorLeft", "cursorRight", 20 | "cursorHome", "cursorEnd", 21 | "cursorWordLeft", "cursorWordRight", 22 | "cursorPageDown", "cursorPageUp", 23 | "cursorTop", "cursorBottom" 24 | ]; 25 | 26 | commandList.forEach(commandName => { 27 | context.subscriptions.push(registerCommand(commandName, op)); 28 | }); 29 | 30 | cursorMoves.forEach(element => { 31 | context.subscriptions.push(vscode.commands.registerCommand( 32 | "emacs."+element, () => { 33 | if (inMarkMode) { 34 | markHasMoved = true; 35 | } 36 | vscode.commands.executeCommand( 37 | inMarkMode ? 38 | element+"Select" : 39 | element 40 | ); 41 | }) 42 | ) 43 | }); 44 | 45 | initMarkMode(context); 46 | } 47 | 48 | export function deactivate(): void { 49 | } 50 | 51 | function initMarkMode(context: vscode.ExtensionContext): void { 52 | context.subscriptions.push(vscode.commands.registerCommand( 53 | 'emacs.enterMarkMode', () => { 54 | if (inMarkMode && !markHasMoved) { 55 | inMarkMode = false; 56 | } else { 57 | initSelection(); 58 | inMarkMode = true; 59 | markHasMoved = false; 60 | } 61 | }) 62 | ); 63 | 64 | context.subscriptions.push(vscode.commands.registerCommand( 65 | 'emacs.exitMarkMode', () => { 66 | const selections = vscode.window.activeTextEditor.selections; 67 | const hasMultipleSelecitons = selections.length > 1; 68 | if (hasMultipleSelecitons) { 69 | const allSelectionsAreEmpty = selections.every(selection => selection.isEmpty); 70 | if (allSelectionsAreEmpty) { 71 | vscode.commands.executeCommand("removeSecondaryCursors"); 72 | } else { 73 | // initSelection() is used here instead of `executeCommand("cancelSelection")` 74 | // because `cancelSelection` command not only cancels selection state 75 | // but also removes secondary cursors though these should remain in this case. 76 | initSelection(); 77 | } 78 | } else { 79 | // This `executeCommand("cancelSelection")` may be able to be replaced with `initSelection()`, 80 | // however, the core command is used here to follow its updates with ease. 81 | vscode.commands.executeCommand("cancelSelection"); 82 | } 83 | 84 | if (inMarkMode) { 85 | inMarkMode = false; 86 | } 87 | }) 88 | ); 89 | } 90 | 91 | function registerCommand(commandName: string, op: Operation): vscode.Disposable { 92 | return vscode.commands.registerCommand("emacs." + commandName, op.getCommand(commandName)); 93 | } 94 | 95 | function initSelection(): void { 96 | // Set new `anchor` and `active` values to all selections so that these are initialized to be empty. 97 | vscode.window.activeTextEditor.selections = vscode.window.activeTextEditor.selections.map(selection => { 98 | const currentPosition: vscode.Position = selection.active; 99 | return new vscode.Selection(currentPosition, currentPosition); 100 | }); 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vscode-emacs-friendly 2 | 3 | This plugin provides emacs keybindings and workflow for Visual Studio Code and is a fork of the great vscode extension by [hiro-sun](https://github.com/hiro-sun/vscode-emacs). 4 | 5 | It merges some of the pull requests in the original and other external helpers that make the extension a little less an exact copy of emacs behavior, and a little more friendly in interacting with the system clipboard and normal vscode interactions. 6 | 7 | The following are some of the changes and enhancements from the original: 8 | 9 | * The clipboard handling is simplified by the removal of the emacs-only kill ring (which was also an unfinished implementation in the original). Copy, Cut, Yank and C-K work with the system clipboard now. 10 | * C+x k to close tab, C+x C-k all tabs 11 | * C+l centers screen on the cursor line 12 | * C+x C+f bound to quick open file 13 | * yank overwrites selection 14 | 15 | 16 | ### Move commands 17 | |Command | Desc | 18 | |--------|------| 19 | | `C-f` | Move forward | 20 | | `C-b` | Move backward | 21 | | `C-n` | Move to the next line | 22 | | `C-p` | Move to the previous line | 23 | | `C-a` | Move to the beginning of line | 24 | | `C-e` | Move to the end of line | 25 | | `M-f` | Move forward by one word unit | 26 | | `M-b` | Move backward by one word unit | 27 | | `M->` | Move to the end of buffer | 28 | | `M-<` | Move to the beginning of buffer | 29 | | `C-v` | Scroll down by one screen unit | 30 | | `M-v` | Scroll up by one screen unit | 31 | | `M-g g` | Jump to line (command palette) | 32 | | `M-g n` | Jump to next error | 33 | | `M-g p` | Jump to previous error | 34 | | `C-l` | Center screen on current line | 35 | 36 | 37 | ### Search Commands 38 | |Command | Desc | 39 | |--------|------| 40 | | `C-s` | Search forward | 41 | | `C-r` | Search backward | 42 | | `A-%` | Replace | 43 | | `C-Enter` | Replace One Match (In replace dialog) | 44 | | `C-M-n` | Add selection to next find match | 45 | 46 | 47 | ### Edit commands 48 | |Command | Desc | 49 | |--------|------| 50 | | `C-d` | Delete right (DEL) | 51 | | `C-h` | Delete left (BACKSPACE) | 52 | | `M-d` | Delete word | 53 | | `M-Bksp` | Delete word left | 54 | | `C-k` | Kill to line end | 55 | | `C-S-Bksp` | Kill entire line | 56 | | `C-o` | open-line | 57 | | `C-w` | Kill region | 58 | | `M-w` | Copy region to kill ring | 59 | | `C-y` | Yank | 60 | | `C-j` | Enter | 61 | | `C-m` | Enter | 62 | | `C-x C-o` | Delete blank lines around | 63 | | `C-x h` | Select All | 64 | | `C-x u` (`C-/`, `C-_`)| Undo | 65 | | `C-;` | Toggle line comment in and out | 66 | | `M-;` | Toggle region comment in and out | 67 | | `C-x C-l` | Convert to lower case | 68 | | `C-x C-u` | Convert to upper case | 69 | 70 | ### Other Commands 71 | |Command | Desc | 72 | |--------|------| 73 | | `C-g` | Cancel | 74 | | `C-space` | Set mark | 75 | | `C-quote` | IntelliSense Suggestion | 76 | | `M-x` | Open command palette | 77 | | `C-M-SPC` | Toggle SideBar visibility | 78 | | `C-x z` | | Toggle Zen Mode | 79 | | `C-x r` | | Open Recent | 80 | 81 | ### File Commands 82 | |Command | Desc | 83 | |--------|------| 84 | | `C-x C-s` | Save | 85 | | `C-x C-w` | Save as | 86 | | `C-x C-n` | Open new window | 87 | 88 | ### Tab / Buffer Manipulation Commands 89 | |Command | Desc | 90 | |--------|------| 91 | | `C-x b` | Switch to another open buffer | 92 | | `C-x C-f` | QuickOpen a file | 93 | | `C-x k` | Close current tab (buffer) | 94 | | `C-x C-k` | Close all tabs | 95 | | `C-x 0` | Close editors in the current group. | 96 | | `C-x 1` | Close editors in other (split) group. | 97 | | `C-x 2` | Split editor horizontal | 98 | | `C-x 3` | Split editor vertical | 99 | | `C-x 4` | Toggle split layout (vertical to horizontal) | 100 | | `C-x o` | Focus other split editor | 101 | 102 | ## Conflicts with default key bindings 103 | - `ctrl+d`: editor.action.addSelectionToNextFindMatch => **Use `ctrl+alt+n` instead**; 104 | - `ctrl+g`: workbench.action.gotoLine => **Use `alt+g g` instead**; 105 | - `ctrl+b`: workbench.action.toggleSidebarVisibility => **Use `ctrl+alt+space` instead**; 106 | - `ctrl+space`: toggleSuggestionDetails, editor.action.triggerSuggest => **Use `ctrl+'` instead**; 107 | - `ctrl+x`: editor.action.clipboardCutAction => **Use `ctrl+w` instead**; 108 | - `ctrl+v`: editor.action.clipboardPasteAction => **Use `ctrl+y` instead**; 109 | - `ctrl+k`: editor.debug.action.showDebugHover, editor.action.trimTrailingWhitespace, editor.action.showHover, editor.action.removeCommentLine, editor.action.addCommentLine, editor.action.openDeclarationToTheSide; 110 | - `ctrl+k z`: workbench.action.toggleZenMode => **Use `ctrl+x z` instead**; 111 | - `ctrl+y`: redo; 112 | - `ctrl+m`: editor.action.toggleTabFocusMode; 113 | - `ctrl+/`: editor.action.commentLine => **Use `ctrl+;` instead**; 114 | - `ctrl+p` & `ctrl+e`: workbench.action.quickOpen => **Use `ctrl+x b` instead**; 115 | - `ctrl+p`: workbench.action.quickOpenNavigateNext => **Use `ctrl+n` instead**. 116 | - `ctrl+o`: workbench.action.files.openFile => **Use `ctrl+x ctrl+f` instead**. 117 | - `ctrl+r`: workbench.action.openRecent => **Use `ctrl+x r` instead**. 118 | 119 | # More information 120 | 121 | The logo is from the great [Pacifica Icon Set](http://bokehlicia.deviantart.com/art/Pacifica-Icons-402508559). 122 | -------------------------------------------------------------------------------- /src/editor.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | // Possible positions when C-l is invoked consequtively 4 | enum RecenterPosition { 5 | Middle, 6 | Top, 7 | Bottom 8 | }; 9 | 10 | export class Editor { 11 | private lastKill: vscode.Position // if kill position stays the same, append to clipboard 12 | private justDidKill: boolean 13 | private centerState: RecenterPosition 14 | 15 | constructor() { 16 | this.justDidKill = false 17 | this.lastKill = null 18 | this.centerState = RecenterPosition.Middle 19 | 20 | vscode.window.onDidChangeActiveTextEditor(event => { 21 | this.lastKill = null 22 | }) 23 | vscode.workspace.onDidChangeTextDocument(event => { 24 | if (!this.justDidKill) { 25 | this.lastKill = null 26 | } 27 | this.justDidKill = false 28 | }) 29 | vscode.window.onDidChangeTextEditorSelection(event => { 30 | this.centerState = RecenterPosition.Middle 31 | }) 32 | } 33 | 34 | static isOnLastLine(): boolean { 35 | return vscode.window.activeTextEditor.selection.active.line == vscode.window.activeTextEditor.document.lineCount - 1 36 | } 37 | 38 | setStatusBarMessage(text: string): vscode.Disposable { 39 | return vscode.window.setStatusBarMessage(text, 1000); 40 | } 41 | 42 | setStatusBarPermanentMessage(text: string): vscode.Disposable { 43 | return vscode.window.setStatusBarMessage(text); 44 | } 45 | 46 | getSelectionRange(): vscode.Range { 47 | let selection = vscode.window.activeTextEditor.selection, 48 | start = selection.start, 49 | end = selection.end; 50 | 51 | return (start.character !== end.character || start.line !== end.line) ? new vscode.Range(start, end) : null; 52 | } 53 | 54 | getSelection(): vscode.Selection { 55 | return vscode.window.activeTextEditor.selection; 56 | } 57 | 58 | getSelectionText(): string { 59 | let r = this.getSelectionRange() 60 | return r ? vscode.window.activeTextEditor.document.getText(r) : '' 61 | } 62 | 63 | setSelection(start: vscode.Position, end: vscode.Position): void { 64 | let editor = vscode.window.activeTextEditor; 65 | editor.selection = new vscode.Selection(start, end); 66 | } 67 | 68 | getCurrentPos(): vscode.Position { 69 | return vscode.window.activeTextEditor.selection.active 70 | } 71 | 72 | // Kill to end of line 73 | async kill(): Promise { 74 | // Ignore whatever we have selected before 75 | await vscode.commands.executeCommand("emacs.exitMarkMode") 76 | 77 | let startPos = this.getCurrentPos(), 78 | isOnLastLine = Editor.isOnLastLine() 79 | 80 | // Move down an entire line (not just the wrapped part), and to the beginning. 81 | await vscode.commands.executeCommand("cursorMove", { to: "down", by: "line", select: false }) 82 | if (!isOnLastLine) { 83 | await vscode.commands.executeCommand("cursorMove", { to: "wrappedLineStart" }) 84 | } 85 | 86 | let endPos = this.getCurrentPos(), 87 | range = new vscode.Range(startPos, endPos), 88 | txt = vscode.window.activeTextEditor.document.getText(range) 89 | 90 | // If there is something other than whitespace in the selection, we do not cut the EOL too 91 | if (!isOnLastLine && !txt.match(/^\s*$/)) { 92 | await vscode.commands.executeCommand("cursorMove", {to: "left", by: "character"}) 93 | endPos = this.getCurrentPos() 94 | } 95 | 96 | // Select it now, cut the selection, remember the position in case of multiple cuts from same spot 97 | this.setSelection(startPos, endPos) 98 | let promise = this.cut(this.lastKill != null && startPos.isEqual(this.lastKill)) 99 | 100 | promise.then(() => { 101 | this.justDidKill = true 102 | this.lastKill = startPos 103 | }) 104 | 105 | return promise 106 | } 107 | 108 | copy(): void { 109 | vscode.env.clipboard.writeText(this.getSelectionText()) 110 | vscode.commands.executeCommand("emacs.exitMarkMode") 111 | } 112 | 113 | async cut(appendClipboard?: boolean): Promise { 114 | if (appendClipboard) { 115 | const text = await vscode.env.clipboard.readText(); 116 | vscode.env.clipboard.writeText(text + this.getSelectionText()) 117 | } else { 118 | vscode.env.clipboard.writeText(this.getSelectionText()) 119 | } 120 | let t = Editor.delete(this.getSelectionRange()); 121 | vscode.commands.executeCommand("emacs.exitMarkMode"); 122 | return t 123 | } 124 | 125 | yank(): Thenable<{}> { 126 | this.justDidKill = false 127 | return Promise.all([ 128 | vscode.commands.executeCommand("editor.action.clipboardPasteAction"), 129 | vscode.commands.executeCommand("emacs.exitMarkMode")]) 130 | } 131 | 132 | undo(): void { 133 | vscode.commands.executeCommand("undo"); 134 | } 135 | 136 | private getFirstBlankLine(range: vscode.Range): vscode.Range { 137 | let doc = vscode.window.activeTextEditor.document; 138 | 139 | if (range.start.line === 0) { 140 | return range; 141 | } 142 | range = doc.lineAt(range.start.line - 1).range; 143 | while (range.start.line > 0 && range.isEmpty) { 144 | range = doc.lineAt(range.start.line - 1).range; 145 | } 146 | if (range.isEmpty) { 147 | return range; 148 | } else { 149 | return doc.lineAt(range.start.line + 1).range; 150 | } 151 | } 152 | 153 | async deleteBlankLines() { 154 | let selection = this.getSelection(), 155 | anchor = selection.anchor, 156 | doc = vscode.window.activeTextEditor.document, 157 | range = doc.lineAt(selection.start.line).range, 158 | nextLine: vscode.Position; 159 | 160 | if (range.isEmpty) { 161 | range = this.getFirstBlankLine(range); 162 | anchor = range.start; 163 | nextLine = range.start; 164 | } else { 165 | nextLine = range.start.translate(1, 0); 166 | } 167 | selection = new vscode.Selection(nextLine, nextLine); 168 | vscode.window.activeTextEditor.selection = selection; 169 | 170 | for (let line = selection.start.line; 171 | line < doc.lineCount - 1 && doc.lineAt(line).range.isEmpty; 172 | ++line) { 173 | 174 | await vscode.commands.executeCommand("deleteRight") 175 | } 176 | vscode.window.activeTextEditor.selection = new vscode.Selection(anchor, anchor) 177 | } 178 | 179 | static delete(range: vscode.Range = null): Thenable { 180 | if (range) { 181 | return vscode.window.activeTextEditor.edit(editBuilder => { 182 | editBuilder.delete(range); 183 | }); 184 | } 185 | } 186 | 187 | deleteLine() : void { 188 | vscode.commands.executeCommand("emacs.exitMarkMode"); // emulate Emacs 189 | vscode.commands.executeCommand("editor.action.deleteLines"); 190 | } 191 | 192 | scrollLineToCenterTopBottom = () => { 193 | const editor = vscode.window.activeTextEditor 194 | const selection = editor.selection 195 | 196 | switch (this.centerState) { 197 | case RecenterPosition.Middle: 198 | this.centerState = RecenterPosition.Top; 199 | editor.revealRange(selection, vscode.TextEditorRevealType.InCenter); 200 | break; 201 | case RecenterPosition.Top: 202 | this.centerState = RecenterPosition.Bottom; 203 | editor.revealRange(selection, vscode.TextEditorRevealType.AtTop); 204 | break; 205 | case RecenterPosition.Bottom: 206 | this.centerState = RecenterPosition.Middle; 207 | // There is no AtBottom, so instead scroll a page up (without moving cursor). 208 | // The current line then ends up as the last line of the window (more or less) 209 | vscode.commands.executeCommand("scrollPageUp"); 210 | break; 211 | } 212 | } 213 | 214 | breakLine() { 215 | vscode.commands.executeCommand("lineBreakInsert"); 216 | vscode.commands.executeCommand("emacs.cursorHome"); 217 | vscode.commands.executeCommand("emacs.cursorDown"); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-emacs-friendly", 3 | "displayName": "Emacs Friendly Keymap", 4 | "description": "Emacs keybindings and selection, friendly interaction with the system clipboard.", 5 | "icon": "emacs_pacifica.png", 6 | "version": "0.9.0", 7 | "publisher": "lfs", 8 | "homepage": "https://github.com/SebastianZaha/vscode-emacs-friendly", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/SebastianZaha/vscode-emacs-friendly" 12 | }, 13 | "bugs": "https://github.com/SebastianZaha/vscode-emacs-friendly/issues", 14 | "engines": { 15 | "vscode": "^1.30.0" 16 | }, 17 | "categories": [ 18 | "Other", 19 | "Keymaps" 20 | ], 21 | "keywords": [ 22 | "emacs", 23 | "shortcuts", 24 | "keybindings", 25 | "behavior", 26 | "selection" 27 | ], 28 | "activationEvents": [ 29 | "*" 30 | ], 31 | "main": "./out/src/extension", 32 | "contributes": { 33 | "keybindings": [ 34 | { 35 | "key": "right", 36 | "command": "emacs.cursorRight", 37 | "when": "editorTextFocus" 38 | }, 39 | { 40 | "key": "ctrl+f", 41 | "command": "emacs.cursorRight", 42 | "when": "editorTextFocus" 43 | }, 44 | { 45 | "key": "ctrl+f", 46 | "command": "emacs.cursorRight", 47 | "when": "terminalFocus" 48 | }, 49 | { 50 | "key": "ctrl+f", 51 | "command": "closeFindWidget", 52 | "when": "editorFocus && findWidgetVisible" 53 | }, 54 | { 55 | "key": "left", 56 | "command": "emacs.cursorLeft", 57 | "when": "editorTextFocus" 58 | }, 59 | { 60 | "key": "ctrl+b", 61 | "command": "emacs.cursorLeft", 62 | "when": "editorTextFocus" 63 | }, 64 | { 65 | "key": "ctrl+b", 66 | "command": "emacs.cursorLeft", 67 | "when": "terminalFocus" 68 | }, 69 | { 70 | "key": "ctrl+b", 71 | "command": "closeFindWidget", 72 | "when": "editorFocus && findWidgetVisible" 73 | }, 74 | { 75 | "key": "up", 76 | "command": "emacs.cursorUp", 77 | "when": "editorTextFocus && !suggestWidgetVisible" 78 | }, 79 | { 80 | "key": "up", 81 | "command": "closeFindWidget", 82 | "when": "editorFocus && findWidgetVisible" 83 | }, 84 | { 85 | "key": "ctrl+p", 86 | "command": "emacs.cursorUp", 87 | "when": "editorTextFocus && !suggestWidgetVisible" 88 | }, 89 | { 90 | "key": "ctrl+p", 91 | "command": "emacs.cursorUp", 92 | "when": "terminalFocus" 93 | }, 94 | { 95 | "key": "ctrl+p", 96 | "command": "closeFindWidget", 97 | "when": "editorFocus && findWidgetVisible" 98 | }, 99 | { 100 | "key": "down", 101 | "command": "emacs.cursorDown", 102 | "when": "editorTextFocus && !suggestWidgetVisible" 103 | }, 104 | { 105 | "key": "down", 106 | "command": "closeFindWidget", 107 | "when": "editorFocus && findWidgetVisible" 108 | }, 109 | { 110 | "key": "ctrl+n", 111 | "command": "emacs.cursorDown", 112 | "when": "editorTextFocus && !suggestWidgetVisible" 113 | }, 114 | { 115 | "key": "ctrl+n", 116 | "command": "emacs.cursorDown", 117 | "when": "terminalFocus" 118 | }, 119 | { 120 | "key": "ctrl+n", 121 | "command": "closeFindWidget", 122 | "when": "editorFocus && findWidgetVisible" 123 | }, 124 | { 125 | "key": "home", 126 | "command": "emacs.cursorHome", 127 | "when": "editorTextFocus" 128 | }, 129 | { 130 | "key": "ctrl+a", 131 | "command": "emacs.cursorHome", 132 | "when": "editorTextFocus" 133 | }, 134 | { 135 | "key": "ctrl+a", 136 | "command": "emacs.cursorHome", 137 | "when": "terminalFocus" 138 | }, 139 | { 140 | "key": "ctrl+a", 141 | "command": "closeFindWidget", 142 | "when": "editorFocus && findWidgetVisible" 143 | }, 144 | { 145 | "key": "end", 146 | "command": "emacs.cursorEnd", 147 | "when": "editorTextFocus" 148 | }, 149 | { 150 | "key": "ctrl+e", 151 | "command": "emacs.cursorEnd", 152 | "when": "editorTextFocus" 153 | }, 154 | { 155 | "key": "ctrl+e", 156 | "command": "emacs.cursorEnd", 157 | "when": "terminalFocus" 158 | }, 159 | { 160 | "key": "ctrl+e", 161 | "command": "closeFindWidget", 162 | "when": "editorFocus && findWidgetVisible" 163 | }, 164 | { 165 | "key": "alt+f", 166 | "command": "emacs.cursorWordRight", 167 | "when": "editorTextFocus" 168 | }, 169 | { 170 | "key": "alt+b", 171 | "command": "emacs.cursorWordLeft", 172 | "when": "editorTextFocus" 173 | }, 174 | { 175 | "key": "pagedown", 176 | "command": "emacs.cursorPageDown", 177 | "when": "editorTextFocus && !suggestWidgetVisible" 178 | }, 179 | { 180 | "key": "pagedown", 181 | "command": "closeFindWidget", 182 | "when": "editorFocus && findWidgetVisible" 183 | }, 184 | { 185 | "key": "ctrl+v", 186 | "command": "emacs.cursorPageDown", 187 | "when": "editorTextFocus && !suggestWidgetVisible" 188 | }, 189 | { 190 | "key": "ctrl+v", 191 | "command": "closeFindWidget", 192 | "when": "editorFocus && findWidgetVisible" 193 | }, 194 | { 195 | "key": "pageup", 196 | "command": "emacs.cursorPageUp", 197 | "when": "editorTextFocus && !suggestWidgetVisible" 198 | }, 199 | { 200 | "key": "pageup", 201 | "command": "closeFindWidget", 202 | "when": "editorFocus && findWidgetVisible" 203 | }, 204 | { 205 | "key": "alt+v", 206 | "command": "emacs.cursorPageUp", 207 | "when": "editorTextFocus && !suggestWidgetVisible" 208 | }, 209 | { 210 | "key": "alt+v", 211 | "command": "closeFindWidget", 212 | "when": "editorFocus && findWidgetVisible" 213 | }, 214 | { 215 | "key": "alt+shift+.", 216 | "command": "emacs.cursorBottom", 217 | "when": "editorTextFocus" 218 | }, 219 | { 220 | "key": "alt+shift+.", 221 | "command": "closeFindWidget", 222 | "when": "editorFocus && findWidgetVisible" 223 | }, 224 | { 225 | "key": "alt+shift+,", 226 | "command": "emacs.cursorTop", 227 | "when": "editorTextFocus" 228 | }, 229 | { 230 | "key": "alt+shift+,", 231 | "command": "closeFindWidget", 232 | "when": "editorFocus && findWidgetVisible" 233 | }, 234 | { 235 | "key": "alt+g g", 236 | "command": "workbench.action.gotoLine" 237 | }, 238 | { 239 | "key": "alt+g n", 240 | "command": "editor.action.marker.next" 241 | }, 242 | { 243 | "key": "alt+g p", 244 | "command": "editor.action.marker.prev" 245 | }, 246 | { 247 | "key": "alt+g g", 248 | "command": "closeFindWidget", 249 | "when": "editorFocus && findWidgetVisible" 250 | }, 251 | { 252 | "key": "ctrl+s", 253 | "command": "actions.find", 254 | "when": "!findWidgetVisible" 255 | }, 256 | { 257 | "key": "alt+shift+5", 258 | "command": "editor.action.startFindReplaceAction", 259 | "when": "editorFocus" 260 | }, 261 | { 262 | "command": "editor.action.replaceOne", 263 | "key": "ctrl+enter", 264 | "when": "editorFocus && findWidgetVisible" 265 | }, 266 | { 267 | "key": "ctrl+s", 268 | "command": "actions.find", 269 | "when": "textInputFocus && findWidgetVisible" 270 | }, 271 | { 272 | "key": "ctrl+s", 273 | "command": "editor.action.nextMatchFindAction", 274 | "when": "findWidgetVisible" 275 | }, 276 | { 277 | "key": "ctrl+r", 278 | "command": "actions.find", 279 | "when": "!findWidgetVisible" 280 | }, 281 | { 282 | "key": "ctrl+r", 283 | "command": "actions.find", 284 | "when": "textInputFocus && findWidgetVisible" 285 | }, 286 | { 287 | "key": "ctrl+r", 288 | "command": "editor.action.previousMatchFindAction", 289 | "when": "findWidgetVisible" 290 | }, 291 | { 292 | "key": "ctrl+alt+n", 293 | "command": "editor.action.addSelectionToNextFindMatch", 294 | "when": "editorFocus" 295 | }, 296 | { 297 | "key": "ctrl+d", 298 | "command": "deleteRight", 299 | "when": "editorTextFocus && !editorReadonly" 300 | }, 301 | { 302 | "key": "ctrl+h", 303 | "command": "deleteLeft", 304 | "when": "editorTextFocus && !editorReadonly" 305 | }, 306 | { 307 | "key": "alt+d", 308 | "command": "deleteWordRight", 309 | "when": "editorTextFocus && !editorReadonly" 310 | }, 311 | { 312 | "key": "ctrl+k", 313 | "command": "emacs.C-k", 314 | "when": "editorTextFocus && !editorReadonly" 315 | }, 316 | { 317 | "key": "ctrl+w", 318 | "command": "emacs.C-w", 319 | "when": "editorTextFocus && !editorReadonly" 320 | }, 321 | { 322 | "key": "ctrl+w", 323 | "command": "closeFindWidget", 324 | "when": "editorFocus && findWidgetVisible" 325 | }, 326 | { 327 | "key": "alt+w", 328 | "command": "emacs.M-w", 329 | "when": "editorTextFocus" 330 | }, 331 | { 332 | "key": "alt+w", 333 | "command": "closeFindWidget", 334 | "when": "editorFocus && findWidgetVisible" 335 | }, 336 | { 337 | "key": "ctrl+y", 338 | "command": "emacs.C-y", 339 | "when": "editorTextFocus && !editorReadonly" 340 | }, 341 | { 342 | "key": "ctrl+y", 343 | "command": "closeFindWidget", 344 | "when": "editorFocus && findWidgetVisible" 345 | }, 346 | { 347 | "key": "ctrl+m", 348 | "command": "emacs.C-j", 349 | "when": "editorTextFocus && !editorReadonly" 350 | }, 351 | { 352 | "key": "ctrl+j", 353 | "command": "emacs.C-j", 354 | "when": "editorTextFocus && !editorReadonly" 355 | }, 356 | { 357 | "key": "ctrl+j", 358 | "command": "closeFindWidget", 359 | "when": "editorFocus && findWidgetVisible" 360 | }, 361 | { 362 | "key": "ctrl+o", 363 | "command": "editor.action.insertLineBefore", 364 | "when": "editorTextFocus && !editorReadonly" 365 | }, 366 | { 367 | "key": "ctrl+x ctrl+o", 368 | "command": "emacs.C-x_C-o", 369 | "when": "editorTextFocus && !editorReadonly" 370 | }, 371 | { 372 | "key": "ctrl+x ctrl+o", 373 | "command": "closeFindWidget", 374 | "when": "editorFocus && findWidgetVisible" 375 | }, 376 | { 377 | "key": "ctrl+x h", 378 | "command": "editor.action.selectAll", 379 | "when": "editorTextFocus" 380 | }, 381 | { 382 | "key": "ctrl+x h", 383 | "command": "closeFindWidget", 384 | "when": "editorFocus && findWidgetVisible" 385 | }, 386 | { 387 | "key": "ctrl+x u", 388 | "command": "emacs.C-/", 389 | "when": "editorTextFocus && !editorReadonly" 390 | }, 391 | { 392 | "key": "ctrl+x u", 393 | "command": "closeFindWidget", 394 | "when": "editorFocus && findWidgetVisible" 395 | }, 396 | { 397 | "key": "ctrl+/", 398 | "command": "emacs.C-/", 399 | "when": "editorTextFocus && !editorReadonly" 400 | }, 401 | { 402 | "key": "ctrl+/", 403 | "command": "closeFindWidget", 404 | "when": "editorFocus && findWidgetVisible" 405 | }, 406 | { 407 | "key": "ctrl+shift+-", 408 | "command": "emacs.C-/", 409 | "when": "editorTextFocus && !editorReadonly" 410 | }, 411 | { 412 | "key": "ctrl+_", 413 | "command": "closeFindWidget", 414 | "when": "editorFocus && findWidgetVisible" 415 | }, 416 | { 417 | "key": "ctrl+x r", 418 | "command": "workbench.action.openRecent" 419 | }, 420 | { 421 | "key": "ctrl+x z", 422 | "command": "workbench.action.toggleZenMode" 423 | }, 424 | { 425 | "key": "ctrl+;", 426 | "command": "editor.action.commentLine", 427 | "when": "editorTextFocus && !editorReadonly" 428 | }, 429 | { 430 | "key": "ctrl+;", 431 | "command": "closeFindWidget", 432 | "when": "editorFocus && findWidgetVisible" 433 | }, 434 | { 435 | "key": "alt+;", 436 | "command": "editor.action.blockComment", 437 | "when": "editorTextFocus && !editorReadonly" 438 | }, 439 | { 440 | "key": "alt+;", 441 | "command": "closeFindWidget", 442 | "when": "editorFocus && findWidgetVisible" 443 | }, 444 | { 445 | "key": "ctrl+l", 446 | "command": "emacs.C-l", 447 | "when": "editorTextFocus" 448 | }, 449 | { 450 | "key": "ctrl+g", 451 | "command": "emacs.C-g", 452 | "when": "editorTextFocus" 453 | }, 454 | { 455 | "key": "ctrl+g", 456 | "command": "closeFindWidget", 457 | "when": "editorFocus && findWidgetVisible" 458 | }, 459 | { 460 | "key": "ctrl+g", 461 | "command": "emacs.exitMarkMode", 462 | "when": "editorTextFocus" 463 | }, 464 | { 465 | "key": "ctrl+g", 466 | "command": "closeReferenceSearchEditor", 467 | "when": "inReferenceSearchEditor && !config.editor.stablePeek" 468 | }, 469 | { 470 | "key": "ctrl+g", 471 | "command": "closeReferenceSearch", 472 | "when": "referenceSearchVisible && !config.editor.stablePeek" 473 | }, 474 | { 475 | "key": "ctrl+g", 476 | "command": "closeBreakpointWidget", 477 | "when": "breakpointWidgetVisible && editorFocus" 478 | }, 479 | { 480 | "key": "ctrl+g", 481 | "command": "leaveSnippet", 482 | "when": "editorTextFocus && inSnippetMode" 483 | }, 484 | { 485 | "key": "ctrl+g", 486 | "command": "closeMarkersNavigation", 487 | "when": "editorFocus && markersNavigationVisible" 488 | }, 489 | { 490 | "key": "ctrl+g", 491 | "command": "closeParameterHints", 492 | "when": "editorTextFocus && parameterHintsVisible" 493 | }, 494 | { 495 | "key": "ctrl+g", 496 | "command": "hideSuggestWidget", 497 | "when": "editorTextFocus && suggestWidgetVisible" 498 | }, 499 | { 500 | "key": "ctrl+g", 501 | "command": "cancelRenameInput", 502 | "when": "editorFocus && renameInputVisible" 503 | }, 504 | { 505 | "key": "ctrl+g", 506 | "command": "closeAccessibilityHelp", 507 | "when": "accessibilityHelpWidgetVisible && editorFocus" 508 | }, 509 | { 510 | "key": "ctrl+g", 511 | "command": "closeReplaceInFilesWidget", 512 | "when": "replaceInputBoxFocus && searchViewletVisible" 513 | }, 514 | { 515 | "key": "ctrl+g", 516 | "command": "workbench.action.closeMessages", 517 | "when": "globalMessageVisible" 518 | }, 519 | { 520 | "key": "ctrl+g", 521 | "command": "workbench.action.closeQuickOpen", 522 | "when": "inQuickOpen" 523 | }, 524 | { 525 | "key": "ctrl+space", 526 | "command": "emacs.enterMarkMode", 527 | "when": "editorTextFocus" 528 | }, 529 | { 530 | "key": "ctrl+x ctrl+f", 531 | "command": "workbench.action.quickOpen" 532 | }, 533 | { 534 | "key": "ctrl+x ctrl+s", 535 | "command": "workbench.action.files.save", 536 | "when": "editorTextFocus" 537 | }, 538 | { 539 | "key": "ctrl+x ctrl+w", 540 | "command": "workbench.action.files.saveAs", 541 | "when": "editorTextFocus" 542 | }, 543 | { 544 | "key": "ctrl+x k", 545 | "command": "workbench.action.closeActiveEditor" 546 | }, 547 | { 548 | "key": "ctrl+x ctrl-k", 549 | "command": "workbench.action.closeAllEditors" 550 | }, 551 | { 552 | "key": "ctrl+x k", 553 | "command": "workbench.action.closeWindow", 554 | "when": "!editorIsOpen" 555 | }, 556 | { 557 | "key": "ctrl+x ctrl+n", 558 | "command": "workbench.action.newWindow" 559 | }, 560 | { 561 | "key": "ctrl+x 0", 562 | "command": "workbench.action.closeEditorsInGroup" 563 | }, 564 | { 565 | "key": "ctrl+x 1", 566 | "command": "workbench.action.closeEditorsInOtherGroups" 567 | }, 568 | { 569 | "key": "ctrl+x 2", 570 | "command": "workbench.action.splitEditorDown" 571 | }, 572 | { 573 | "key": "ctrl+x 3", 574 | "command": "workbench.action.splitEditorRight" 575 | }, 576 | { 577 | "key": "ctrl+x 4", 578 | "command": "workbench.action.toggleEditorGroupLayout" 579 | }, 580 | { 581 | "key": "ctrl+x o", 582 | "command": "workbench.action.navigateEditorGroups" 583 | }, 584 | { 585 | "key": "ctrl+p", 586 | "command": "showPrevParameterHint", 587 | "when": "editorTextFocus && parameterHintsVisible" 588 | }, 589 | { 590 | "key": "ctrl+n", 591 | "command": "showNextParameterHint", 592 | "when": "editorTextFocus && parameterHintsVisible" 593 | }, 594 | { 595 | "key": "ctrl+p", 596 | "command": "selectPrevQuickFix", 597 | "when": "editorFocus && quickFixWidgetVisible" 598 | }, 599 | { 600 | "key": "ctrl+n", 601 | "command": "selectNextQuickFix", 602 | "when": "editorFocus && quickFixWidgetVisible" 603 | }, 604 | { 605 | "key": "ctrl+p", 606 | "command": "selectPrevSuggestion", 607 | "when": "editorTextFocus && suggestWidgetVisible" 608 | }, 609 | { 610 | "key": "ctrl+n", 611 | "command": "selectNextSuggestion", 612 | "when": "editorTextFocus && suggestWidgetVisible" 613 | }, 614 | { 615 | "key": "ctrl+p", 616 | "command": "workbench.action.quickOpenNavigatePrevious", 617 | "when": "inQuickOpen" 618 | }, 619 | { 620 | "key": "ctrl+n", 621 | "command": "workbench.action.quickOpenNavigateNext", 622 | "when": "inQuickOpen" 623 | }, 624 | { 625 | "key": "ctrl+'", 626 | "command": "editor.action.triggerSuggest", 627 | "when": "editorTextFocus" 628 | }, 629 | { 630 | "key": "ctrl+'", 631 | "command": "toggleSuggestionDetails", 632 | "when": "editorTextFocus && suggestWidgetVisible" 633 | }, 634 | { 635 | "key": "ctrl+shift+'", 636 | "command": "editor.action.triggerParameterHints", 637 | "when": "editorTextFocus" 638 | }, 639 | { 640 | "key": "alt+x", 641 | "command": "workbench.action.showCommands" 642 | }, 643 | { 644 | "key": "ctrl+alt+space", 645 | "command": "workbench.action.toggleSidebarVisibility" 646 | }, 647 | { 648 | "key": "ctrl+x b", 649 | "command": "workbench.action.showAllEditors" 650 | }, 651 | { 652 | "key": "ctrl+shift+backspace", 653 | "command": "emacs.C-S_bs", 654 | "when": "editorTextFocus" 655 | }, 656 | { 657 | "key": "ctrl+x ctrl+l", 658 | "command": "editor.action.transformToLowercase", 659 | "when": "editorTextFocus && !editorReadonly" 660 | }, 661 | { 662 | "key": "ctrl+x ctrl+u", 663 | "command": "editor.action.transformToUppercase", 664 | "when": "editorTextFocus && !editorReadonly" 665 | }, 666 | { 667 | "key": "alt+backspace", 668 | "command": "deleteWordLeft", 669 | "when": "editorTextFocus && !editorReadonly" 670 | } 671 | ] 672 | }, 673 | "scripts": { 674 | "vscode:prepublish": "tsc -p ./", 675 | "compile": "tsc -watch -p ./", 676 | "postinstall": "node ./node_modules/vscode/bin/install", 677 | "lint": "tslint -p tslint.json --type-check **/*.ts" 678 | }, 679 | "devDependencies": { 680 | "@types/mocha": "^2.2.41", 681 | "@types/node": "^6.0.81", 682 | "mocha": "^6.1.4", 683 | "typescript": "^2.4.1", 684 | "vscode": "^1.1.34" 685 | }, 686 | "dependencies": {} 687 | } 688 | --------------------------------------------------------------------------------