mirror of
https://github.com/VSCodeVim/Vim.git
synced 2024-08-18 00:30:27 +03:00
Read key remappings from .vimrc (#3908)
Read key remapping commands from $HOME/.vimrc, $HOME/_vimrc, or a user-specified Vim configuration file. For each, build an IKeyRemapping object and append it to the appropriate collection, _if_ doing so will not override a remapping specified in the VS Code settings. Partially addresses #463. This implementation borrows heavily from Sheepolution/vimrc-to-json. * Add `editVimrc` command * Add .vimrc validator, correct usage of new config names * Source .vimrc automatically after saving it
This commit is contained in:
parent
66d507bb3d
commit
3158194561
@ -46,8 +46,6 @@ VSCodeVim is a Vim emulator for [Visual Studio Code](https://code.visualstudio.c
|
||||
|
||||
VSCodeVim is automatically enabled following [installation](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim) and reloading of VS Code.
|
||||
|
||||
> :warning: Vimscript is _not_ supported; therefore, we are _not_ able to load your `.vimrc` or use `.vim` plugins. You have to replicate these using our [Settings](#settings) and [Emulated plugins](#-emulated-plugins).
|
||||
|
||||
### Mac
|
||||
|
||||
To enable key-repeating execute the following in your Terminal and restart VS Code:
|
||||
@ -365,6 +363,12 @@ Configuration settings that have been copied from vim. Vim settings are loaded i
|
||||
| vim.whichwrap | Controls wrapping at beginning and end of line. Comma-separated set of keys that should wrap to next/previous line. Arrow keys are represented by `[` and `]` in insert mode, `<` and `>` in normal and visual mode. To wrap "everything", set this to `h,l,<,>,[,]`. | String | `` |
|
||||
| vim.report | Threshold for reporting number of lines changed. | Number | 2 |
|
||||
|
||||
## .vimrc support
|
||||
|
||||
> :warning: .vimrc support is currently experimental. Only remaps are supported, and you may experience bugs. Please [report them](https://github.com/VSCodeVim/Vim/issues/new?template=bug_report.md)!
|
||||
|
||||
Set `vim.vimrc.enable` to `true` and set `vim.vimrc.path` appropriately.
|
||||
|
||||
## 🖱️ Multi-Cursor Mode
|
||||
|
||||
> :warning: Multi-Cursor mode is experimental. Please report issues in our [feedback thread.](https://github.com/VSCodeVim/Vim/issues/824)
|
||||
|
@ -2848,4 +2848,4 @@ The first commit to this project was a little over 3 years ago, and what a journ
|
||||
- add gulp + tslint [\#6](https://github.com/VSCodeVim/Vim/pull/6) ([jpoon](https://github.com/jpoon))
|
||||
- command line mode refactoring [\#5](https://github.com/VSCodeVim/Vim/pull/5) ([guillermooo](https://github.com/guillermooo))
|
||||
- Navigation mode [\#4](https://github.com/VSCodeVim/Vim/pull/4) ([jpoon](https://github.com/jpoon))
|
||||
- Add ex mode [\#3](https://github.com/VSCodeVim/Vim/pull/3) ([guillermooo](https://github.com/guillermooo))
|
||||
- Add ex mode [\#3](https://github.com/VSCodeVim/Vim/pull/3) ([guillermooo](https://github.com/guillermooo))
|
||||
|
18
extension.ts
18
extension.ts
@ -6,6 +6,7 @@
|
||||
import './src/actions/include-all';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
|
||||
import { CompositionState } from './src/state/compositionState';
|
||||
import { EditorIdentity } from './src/editorIdentity';
|
||||
@ -24,6 +25,7 @@ import { configuration } from './src/configuration/configuration';
|
||||
import { globalState } from './src/state/globalState';
|
||||
import { taskQueue } from './src/taskQueue';
|
||||
import { Register } from './src/register/register';
|
||||
import { vimrc } from './src/configuration/vimrc';
|
||||
|
||||
let extensionContext: vscode.ExtensionContext;
|
||||
let previousActiveEditorId: EditorIdentity | null = null;
|
||||
@ -87,6 +89,7 @@ async function loadConfiguration() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
// before we do anything else,
|
||||
// we need to load the configuration first
|
||||
@ -207,6 +210,16 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
false
|
||||
);
|
||||
|
||||
registerEventListener(context, vscode.workspace.onDidSaveTextDocument, async document => {
|
||||
if (
|
||||
configuration.vimrc.enable &&
|
||||
path.relative(document.fileName, configuration.vimrc.path) === ''
|
||||
) {
|
||||
await configuration.load();
|
||||
vscode.window.showInformationMessage('Sourced new .vimrc');
|
||||
}
|
||||
});
|
||||
|
||||
// window events
|
||||
registerEventListener(
|
||||
context,
|
||||
@ -391,6 +404,11 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
toggleExtension(configuration.disableExtension, compositionState);
|
||||
});
|
||||
|
||||
registerCommand(context, 'vim.editVimrc', async () => {
|
||||
const document = await vscode.workspace.openTextDocument(configuration.vimrc.path);
|
||||
await vscode.window.showTextDocument(document);
|
||||
});
|
||||
|
||||
for (const boundKey of configuration.boundKeyCombinations) {
|
||||
registerCommand(context, boundKey.command, () => handleKeyEvent(`${boundKey.key}`));
|
||||
}
|
||||
|
25
package-lock.json
generated
25
package-lock.json
generated
@ -141,20 +141,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.138",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.138.tgz",
|
||||
"integrity": "sha512-A4uJgHz4hakwNBdHNPdxOTkYmXNgmUAKLbXZ7PKGslgeV0Mb8P3BlbYfPovExek1qnod4pDfRbxuzcVs3dlFLg==",
|
||||
"version": "4.14.144",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.144.tgz",
|
||||
"integrity": "sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash.escaperegexp": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash.escaperegexp/-/lodash.escaperegexp-4.1.6.tgz",
|
||||
"integrity": "sha512-uENiqxLlqh6RzeE1cC6Z2gHqakToN9vKlTVCFkSVjAfeMeh2fY0916tHwJHeeKs28qB/hGYvKuampGYH5QDVCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "5.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
|
||||
@ -1888,6 +1879,11 @@
|
||||
"map-cache": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"fs": {
|
||||
"version": "0.0.1-security",
|
||||
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
|
||||
"integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ="
|
||||
},
|
||||
"fs-mkdirp-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
|
||||
@ -3524,11 +3520,6 @@
|
||||
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.escaperegexp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
||||
"integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
|
||||
},
|
||||
"lodash.template": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
|
||||
|
18
package.json
18
package.json
@ -50,6 +50,10 @@
|
||||
{
|
||||
"command": "vim.showQuickpickCmdLine",
|
||||
"title": "Vim: Show Command Line"
|
||||
},
|
||||
{
|
||||
"command": "vim.editVimrc",
|
||||
"title": "Vim: Edit .vimrc"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
@ -750,6 +754,15 @@
|
||||
"default": "",
|
||||
"scope": "machine-overridable"
|
||||
},
|
||||
"vim.vimrc.enable": {
|
||||
"type": "boolean",
|
||||
"description": "Use key mappings from a .vimrc file.",
|
||||
"default": "true"
|
||||
},
|
||||
"vim.vimrc.path": {
|
||||
"type": "string",
|
||||
"description": "Path to a Vim configuration file. If unset, it will check for $HOME/.vimrc or $HOME/_vimrc."
|
||||
},
|
||||
"vim.substituteGlobalFlag": {
|
||||
"type": "boolean",
|
||||
"markdownDescription": "Automatically apply the global flag, `/g`, to substitute commands. When set to true, use `/g` to mean only first match should be replaced.",
|
||||
@ -897,7 +910,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"diff-match-patch": "1.0.4",
|
||||
"lodash.escaperegexp": "4.1.2",
|
||||
"fs": "0.0.1-security",
|
||||
"lodash": "^4.17.15",
|
||||
"neovim": "4.5.0",
|
||||
"untildify": "4.0.0",
|
||||
"winston": "3.2.1",
|
||||
@ -907,7 +921,7 @@
|
||||
"devDependencies": {
|
||||
"@types/diff": "4.0.2",
|
||||
"@types/diff-match-patch": "1.0.32",
|
||||
"@types/lodash.escaperegexp": "4.1.6",
|
||||
"@types/lodash": "^4.14.144",
|
||||
"@types/mocha": "5.2.7",
|
||||
"@types/node": "12.12.7",
|
||||
"@types/sinon": "7.5.0",
|
||||
|
@ -6,7 +6,11 @@ export function parseSortCommandArgs(args: string): node.SortCommand {
|
||||
return new node.SortCommand({ reverse: false, ignoreCase: false, unique: false });
|
||||
}
|
||||
|
||||
let scannedArgs: node.ISortCommandArguments = { reverse: false, ignoreCase: false, unique: false };
|
||||
let scannedArgs: node.ISortCommandArguments = {
|
||||
reverse: false,
|
||||
ignoreCase: false,
|
||||
unique: false,
|
||||
};
|
||||
let scanner = new Scanner(args);
|
||||
const c = scanner.next();
|
||||
scannedArgs.reverse = c === '!';
|
||||
|
@ -4,7 +4,7 @@ import { VimState } from '../../state/vimState';
|
||||
import { configuration } from './../../configuration/configuration';
|
||||
import { VisualBlockMode } from './../../mode/modes';
|
||||
import { TextEditor } from './../../textEditor';
|
||||
import escapeRegExp = require('lodash.escaperegexp');
|
||||
import * as _ from 'lodash';
|
||||
|
||||
enum PositionDiffType {
|
||||
Offset,
|
||||
@ -874,7 +874,7 @@ export class Position extends vscode.Position {
|
||||
}
|
||||
|
||||
private static makeWordRegex(characterSet: string): RegExp {
|
||||
let escaped = characterSet && escapeRegExp(characterSet).replace(/-/g, '\\-');
|
||||
let escaped = characterSet && _.escapeRegExp(characterSet).replace(/-/g, '\\-');
|
||||
let segments: string[] = [];
|
||||
|
||||
segments.push(`([^\\s${escaped}]+)`);
|
||||
@ -886,7 +886,7 @@ export class Position extends vscode.Position {
|
||||
}
|
||||
|
||||
private static makeCamelCaseWordRegex(characterSet: string): RegExp {
|
||||
const escaped = characterSet && escapeRegExp(characterSet).replace(/-/g, '\\-');
|
||||
const escaped = characterSet && _.escapeRegExp(characterSet).replace(/-/g, '\\-');
|
||||
const segments: string[] = [];
|
||||
|
||||
// old versions of VSCode before 1.31 will crash when trying to parse a regex with a lookbehind
|
||||
@ -1028,7 +1028,7 @@ export class Position extends vscode.Position {
|
||||
|
||||
// Symbols in vim.iskeyword or editor.wordSeparators
|
||||
// are treated as CharKind.Punctuation
|
||||
const escapedKeywordChars = escapeRegExp(keywordChars).replace(/-/g, '\\-');
|
||||
const escapedKeywordChars = _.escapeRegExp(keywordChars).replace(/-/g, '\\-');
|
||||
codePointRangePatterns[Number(CharKind.Punctuation)].push(escapedKeywordChars);
|
||||
|
||||
const codePointRanges = codePointRangePatterns.map(patterns => patterns.join(''));
|
||||
|
@ -5,6 +5,7 @@ import { ValidatorResults } from './iconfigurationValidator';
|
||||
import { VsCodeContext } from '../util/vscode-context';
|
||||
import { configurationValidator } from './configurationValidator';
|
||||
import { decoration } from './decoration';
|
||||
import { vimrc } from './vimrc';
|
||||
import {
|
||||
IConfiguration,
|
||||
IKeyRemapping,
|
||||
@ -86,6 +87,10 @@ class Configuration implements IConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.vimrc.enable) {
|
||||
vimrc.load(this);
|
||||
}
|
||||
|
||||
this.leader = Notation.NormalizeKey(this.leader, this.leaderDefault);
|
||||
|
||||
const validatorResults = await configurationValidator.validate(configuration);
|
||||
@ -303,6 +308,11 @@ class Configuration implements IConfiguration {
|
||||
enableNeovim = false;
|
||||
neovimPath = '';
|
||||
|
||||
vimrc = {
|
||||
enable: false,
|
||||
path: '',
|
||||
};
|
||||
|
||||
digraphs = {};
|
||||
|
||||
gdefault = false;
|
||||
|
@ -3,6 +3,7 @@ import { IConfigurationValidator, ValidatorResults } from './iconfigurationValid
|
||||
import { InputMethodSwitcherConfigurationValidator } from './validators/inputMethodSwitcherValidator';
|
||||
import { NeovimValidator } from './validators/neovimValidator';
|
||||
import { RemappingValidator } from './validators/remappingValidator';
|
||||
import { VimrcValidator } from './validators/vimrcValidator';
|
||||
|
||||
class ConfigurationValidator {
|
||||
private _validators: IConfigurationValidator[];
|
||||
@ -12,6 +13,7 @@ class ConfigurationValidator {
|
||||
new InputMethodSwitcherConfigurationValidator(),
|
||||
new NeovimValidator(),
|
||||
new RemappingValidator(),
|
||||
new VimrcValidator(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,12 @@ export interface IKeyRemapping {
|
||||
before: string[];
|
||||
after?: string[];
|
||||
commands?: ({ command: string; args: any[] } | string)[];
|
||||
source?: 'vscode' | 'vimrc';
|
||||
}
|
||||
|
||||
export interface IVimrcKeyRemapping {
|
||||
keyRemapping: IKeyRemapping;
|
||||
keyRemappingType: string;
|
||||
}
|
||||
|
||||
export interface IAutoSwitchInputMethod {
|
||||
@ -287,6 +293,14 @@ export interface IConfiguration {
|
||||
enableNeovim: boolean;
|
||||
neovimPath: string;
|
||||
|
||||
/**
|
||||
* .vimrc
|
||||
*/
|
||||
vimrc: {
|
||||
enable: boolean;
|
||||
path: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Automatically apply the `/g` flag to substitute commands.
|
||||
*/
|
||||
|
@ -39,5 +39,5 @@ export class ValidatorResults {
|
||||
|
||||
export interface IConfigurationValidator {
|
||||
validate(config: IConfiguration): Promise<ValidatorResults>;
|
||||
disable(config: IConfiguration);
|
||||
disable(config: IConfiguration): void;
|
||||
}
|
||||
|
23
src/configuration/validators/vimrcValidator.ts
Normal file
23
src/configuration/validators/vimrcValidator.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import * as fs from 'fs';
|
||||
import { IConfiguration } from '../iconfiguration';
|
||||
import { IConfigurationValidator, ValidatorResults } from '../iconfigurationValidator';
|
||||
import { vimrc } from '../vimrc';
|
||||
|
||||
export class VimrcValidator implements IConfigurationValidator {
|
||||
async validate(config: IConfiguration): Promise<ValidatorResults> {
|
||||
const result = new ValidatorResults();
|
||||
|
||||
if (config.vimrc.enable && !fs.existsSync(vimrc.vimrcPath)) {
|
||||
result.append({
|
||||
level: 'error',
|
||||
message: `.vimrc not found at ${config.vimrc.path}`,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
disable(config: IConfiguration): void {
|
||||
// no-op
|
||||
}
|
||||
}
|
108
src/configuration/vimrc.ts
Normal file
108
src/configuration/vimrc.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { IConfiguration, IVimrcKeyRemapping } from './iconfiguration';
|
||||
import { vimrcKeyRemappingBuilder } from './vimrcKeyRemappingBuilder';
|
||||
|
||||
class VimrcImpl {
|
||||
private _vimrcPath: string;
|
||||
public get vimrcPath(): string {
|
||||
return this._vimrcPath;
|
||||
}
|
||||
|
||||
public load(config: IConfiguration) {
|
||||
const _path = config.vimrc.path
|
||||
? VimrcImpl.expandHome(config.vimrc.path)
|
||||
: VimrcImpl.findDefaultVimrc();
|
||||
if (!_path || !fs.existsSync(_path)) {
|
||||
// TODO: we may want to offer to create the file for them
|
||||
throw new Error(`Unable to find .vimrc file`);
|
||||
}
|
||||
this._vimrcPath = _path;
|
||||
|
||||
// Remove all the old remappings from the .vimrc file
|
||||
VimrcImpl.removeAllRemapsFromConfig(config);
|
||||
|
||||
// Add the new remappings
|
||||
const lines = fs.readFileSync(config.vimrc.path, { encoding: 'utf8' }).split(/\r?\n/);
|
||||
for (const line of lines) {
|
||||
const remap = vimrcKeyRemappingBuilder.build(line);
|
||||
if (remap) {
|
||||
VimrcImpl.addRemapToConfig(config, remap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a remapping from .vimrc to the given configuration
|
||||
*/
|
||||
private static addRemapToConfig(config: IConfiguration, remap: IVimrcKeyRemapping): void {
|
||||
const remaps = (() => {
|
||||
switch (remap.keyRemappingType) {
|
||||
case 'nmap':
|
||||
return config.normalModeKeyBindings;
|
||||
case 'vmap':
|
||||
return config.visualModeKeyBindings;
|
||||
case 'imap':
|
||||
return config.insertModeKeyBindings;
|
||||
case 'nnoremap':
|
||||
return config.normalModeKeyBindingsNonRecursive;
|
||||
case 'vnoremap':
|
||||
return config.visualModeKeyBindingsNonRecursive;
|
||||
case 'inoremap':
|
||||
return config.insertModeKeyBindingsNonRecursive;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
// Don't override a mapping present in settings.json; those are more specific to VSCodeVim.
|
||||
if (remaps && !remaps.some(r => _.isEqual(r.before, remap!.keyRemapping.before))) {
|
||||
remaps.push(remap.keyRemapping);
|
||||
}
|
||||
}
|
||||
|
||||
private static removeAllRemapsFromConfig(config: IConfiguration): void {
|
||||
const remapCollections = [
|
||||
config.normalModeKeyBindings,
|
||||
config.visualModeKeyBindings,
|
||||
config.insertModeKeyBindings,
|
||||
config.normalModeKeyBindingsNonRecursive,
|
||||
config.visualModeKeyBindingsNonRecursive,
|
||||
config.insertModeKeyBindingsNonRecursive,
|
||||
];
|
||||
for (const remaps of remapCollections) {
|
||||
_.remove(remaps, remap => remap.source === 'vimrc');
|
||||
}
|
||||
}
|
||||
|
||||
private static findDefaultVimrc(): string | undefined {
|
||||
if (process.env.HOME) {
|
||||
let vimrcPath = path.join(process.env.HOME, '.vimrc');
|
||||
if (fs.existsSync(vimrcPath)) {
|
||||
return vimrcPath;
|
||||
}
|
||||
|
||||
vimrcPath = path.join(process.env.HOME, '_vimrc');
|
||||
if (fs.existsSync(vimrcPath)) {
|
||||
return vimrcPath;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private static expandHome(filePath: string): string {
|
||||
if (!process.env.HOME) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
if (!filePath.startsWith('~')) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
return path.join(process.env.HOME, filePath.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
export const vimrc = new VimrcImpl();
|
67
src/configuration/vimrcKeyRemappingBuilder.ts
Normal file
67
src/configuration/vimrcKeyRemappingBuilder.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { IKeyRemapping, IVimrcKeyRemapping } from './iconfiguration';
|
||||
|
||||
class VimrcKeyRemappingBuilderImpl {
|
||||
private static readonly KEY_REMAPPING_REG_EX = /(^.*map)\s([\S]+)\s+([\S]+)$/;
|
||||
private static readonly KEY_LIST_REG_EX = /(<[^>]+>|.)/g;
|
||||
private static readonly COMMAND_REG_EX = /(:\w+)/;
|
||||
|
||||
/**
|
||||
* @returns A remapping if the given `line` parses to one, and `undefined` otherwise.
|
||||
*/
|
||||
public build(line: string): IVimrcKeyRemapping | undefined {
|
||||
const matches = VimrcKeyRemappingBuilderImpl.KEY_REMAPPING_REG_EX.exec(line);
|
||||
if (!matches || matches.length < 4) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const type = matches[1];
|
||||
const before = matches[2];
|
||||
const after = matches[3];
|
||||
|
||||
let mapping: IKeyRemapping;
|
||||
if (VimrcKeyRemappingBuilderImpl.isCommand(after)) {
|
||||
mapping = {
|
||||
before: VimrcKeyRemappingBuilderImpl.buildKeyList(before),
|
||||
commands: [after],
|
||||
source: 'vimrc',
|
||||
};
|
||||
} else {
|
||||
mapping = {
|
||||
before: VimrcKeyRemappingBuilderImpl.buildKeyList(before),
|
||||
after: VimrcKeyRemappingBuilderImpl.buildKeyList(after),
|
||||
source: 'vimrc',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
keyRemapping: mapping,
|
||||
keyRemappingType: type,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` if this remaps a key sequence to a `:` command
|
||||
*/
|
||||
private static isCommand(commandString: string): boolean {
|
||||
const matches = VimrcKeyRemappingBuilderImpl.COMMAND_REG_EX.exec(commandString);
|
||||
if (matches) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static buildKeyList(keyString: string): string[] {
|
||||
let keyList: string[] = [];
|
||||
let matches: RegExpMatchArray | null = null;
|
||||
do {
|
||||
matches = VimrcKeyRemappingBuilderImpl.KEY_LIST_REG_EX.exec(keyString);
|
||||
if (matches) {
|
||||
keyList.push(matches[0]);
|
||||
}
|
||||
} while (matches);
|
||||
|
||||
return keyList;
|
||||
}
|
||||
}
|
||||
|
||||
export const vimrcKeyRemappingBuilder = new VimrcKeyRemappingBuilderImpl();
|
@ -161,7 +161,7 @@ suite('cmd_line/search command', () => {
|
||||
await modeHandler.handleMultipleKeyEvents(['<Esc>', ':', '<C-r>', '<C-w>']);
|
||||
const statusBar = StatusBar.Get().trim();
|
||||
assert.equal(statusBar, ':abc|', 'Failed to insert word');
|
||||
});
|
||||
});
|
||||
|
||||
test('<C-r> <C-w> insert right word of cursor on command line', async () => {
|
||||
await modeHandler.handleMultipleKeyEvents('i::abc'.split(''));
|
||||
|
69
test/configuration/vimrcKeyRemappingBuilder.test.ts
Normal file
69
test/configuration/vimrcKeyRemappingBuilder.test.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import * as assert from 'assert';
|
||||
import { IKeyRemapping, IVimrcKeyRemapping } from '../../src/configuration/iconfiguration';
|
||||
import { vimrcKeyRemappingBuilder } from '../../src/configuration/vimrcKeyRemappingBuilder';
|
||||
|
||||
suite('VimrcKeyRemappingBuilder', () => {
|
||||
test('Build IKeyRemapping objects from .vimrc lines', () => {
|
||||
const testCases = [
|
||||
{
|
||||
vimrcLine: 'nnoremap <C-h> <<',
|
||||
keyRemapping: {
|
||||
before: ['<C-h>'],
|
||||
after: ['<', '<'],
|
||||
source: 'vimrc',
|
||||
},
|
||||
keyRemappingType: 'nnoremap',
|
||||
expectNull: false,
|
||||
},
|
||||
{
|
||||
vimrcLine: 'imap jj <Esc>',
|
||||
keyRemapping: {
|
||||
before: ['j', 'j'],
|
||||
after: ['<Esc>'],
|
||||
source: 'vimrc',
|
||||
},
|
||||
keyRemappingType: 'imap',
|
||||
expectNull: false,
|
||||
},
|
||||
{
|
||||
vimrcLine: 'vnoremap <leader>" c""<Esc>P',
|
||||
keyRemapping: {
|
||||
before: ['<leader>', '"'],
|
||||
after: ['c', '"', '"', '<Esc>', 'P'],
|
||||
source: 'vimrc',
|
||||
},
|
||||
keyRemappingType: 'vnoremap',
|
||||
expectNull: false,
|
||||
},
|
||||
{
|
||||
// Mapping with a command
|
||||
vimrcLine: 'nnoremap <C-s> :w',
|
||||
keyRemapping: {
|
||||
before: ['<C-s>'],
|
||||
commands: [':w'],
|
||||
source: 'vimrc',
|
||||
},
|
||||
keyRemappingType: 'nnoremap',
|
||||
expectNull: false,
|
||||
},
|
||||
{
|
||||
// Ignore non-mapping lines
|
||||
vimrcLine: 'set scrolloff=8',
|
||||
expectNull: true,
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const vimrcKeyRemapping: IVimrcKeyRemapping | undefined = vimrcKeyRemappingBuilder.build(
|
||||
testCase.vimrcLine
|
||||
);
|
||||
|
||||
if (testCase.expectNull) {
|
||||
assert.strictEqual(vimrcKeyRemapping, undefined);
|
||||
} else {
|
||||
assert.deepStrictEqual(vimrcKeyRemapping!.keyRemapping, testCase.keyRemapping);
|
||||
assert.strictEqual(vimrcKeyRemapping!.keyRemappingType, testCase.keyRemappingType);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
@ -81,9 +81,13 @@ export class Configuration implements IConfiguration {
|
||||
foldfix = false;
|
||||
disableExtension = false;
|
||||
enableNeovim = false;
|
||||
neovimPath = '';
|
||||
gdefault = false;
|
||||
substituteGlobalFlag = false; // Deprecated in favor of gdefault
|
||||
neovimPath = 'nvim';
|
||||
vimrc = {
|
||||
enable: false,
|
||||
path: '',
|
||||
};
|
||||
cursorStylePerMode: IModeSpecificStrings<string> = {
|
||||
normal: 'line',
|
||||
insert: 'block',
|
||||
|
Loading…
Reference in New Issue
Block a user