Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Substitute global flag (like Vim's gdefault) #1909

Merged
merged 7 commits into from
Jul 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ These settings are specific to VSCodeVim.
#### `"vim.useSolidBlockCursor"`
We have removed this option, due to it making VSCodeVim's performance suffer immensely.

#### `"vim.substituteGlobalFlag"`
* Similar to Vim's `gdefault` setting.
* `/g` flag in a substitute command replaces all occurrences in the line.
Without this argument, replacement occurs only for the first occurrence in each line.
* When `"vim.substituteGlobalFlag"` is `true`, the 'g' is default on.
This means that all matches in a line are substituted instead of one.
When a 'g' flag is given to a ":substitute" command, this will toggle the substitution
of all or one match.

#### `"vim.useCtrlKeys"`
* Enable Vim ctrl keys overriding common VS Code operations (eg. copy, paste, find, etc). Enabling this setting will:
* `ctrl+c`, `ctrl+[` => `<Esc>`
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,11 @@
"type": "boolean",
"description": "Get rid of that annoying message that shows up everytime you make a new file",
"default": false
},
"vim.substituteGlobalFlag": {
"type": "boolean",
"description": "Automatically apply the global flag, /g, to substitute commands. When set to true, use /g to mean only first match should be replaced.",
"default": "false"
}
}
}
Expand Down Expand Up @@ -566,4 +571,4 @@
"typescript": "^2.3.2",
"vscode": "^1.0.5"
}
}
}
13 changes: 11 additions & 2 deletions src/cmd_line/commands/substitute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as node from '../node';
import * as token from '../token';
import { ModeHandler } from '../../mode/modeHandler';
import { TextEditor } from '../../textEditor';
import { Configuration } from '../../configuration/configuration';

export interface ISubstituteCommandArguments extends node.ICommandArgs {
pattern: string;
Expand Down Expand Up @@ -62,8 +63,16 @@ export class SubstituteCommand extends node.CommandBase {
getRegex(args: ISubstituteCommandArguments, modeHandler: ModeHandler) {
let jsRegexFlags = '';

if (args.flags & SubstituteFlags.ReplaceAll) {
jsRegexFlags += 'g';
if (Configuration.substituteGlobalFlag === true) {
// the gdefault flag is on, then /g if on by default and /g negates that
if (!(args.flags & SubstituteFlags.ReplaceAll)) {
jsRegexFlags += 'g';
}
} else {
// the gdefault flag is off, then /g means replace all
if (args.flags & SubstituteFlags.ReplaceAll) {
jsRegexFlags += 'g';
}
}

if (args.flags & SubstituteFlags.IgnoreCase) {
Expand Down
5 changes: 5 additions & 0 deletions src/configuration/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ class ConfigurationClass {
neovimPath = 'nvim';

disableAnnoyingNeovimMessage = false;

/**
* Automatically apply the /g flag to substitute commands.
*/
substituteGlobalFlag = false;
}

function overlapSetting(args: {
Expand Down
2 changes: 2 additions & 0 deletions src/neovim/nvimUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export class Neovim {
if (Configuration.expandtab) {
await vscode.commands.executeCommand('editor.action.indentationToTabs');
}

await nvim.setOption('gdefault', Configuration.substituteGlobalFlag === true);
await buf.setLines(0, -1, true, TextEditor.getText().split('\n'));
const [rangeStart, rangeEnd] = [
Position.EarlierOf(vimState.cursorPosition, vimState.cursorStartPosition),
Expand Down
104 changes: 104 additions & 0 deletions test/cmd_line/substitute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ModeHandler } from '../../src/mode/modeHandler';
import { setupWorkspace, cleanUpWorkspace, assertEqualLines } from './../testUtils';
import { runCmdLine } from '../../src/cmd_line/main';
import { getAndUpdateModeHandler } from '../../extension';
import { Configuration } from '../../src/configuration/configuration';

suite('Basic substitute', () => {
let modeHandler: ModeHandler;
Expand Down Expand Up @@ -102,4 +103,107 @@ suite('Basic substitute', () => {

assertEqualLines(['dbc', 'dbc', 'abc']);
});

suite('Effects of substituteGlobalFlag=true', () => {
let originalGlobalFlag = false;

setup(async () => {
originalGlobalFlag = Configuration.substituteGlobalFlag;
Configuration.substituteGlobalFlag = true;
});

teardown(async () => {
Configuration.substituteGlobalFlag = originalGlobalFlag;
});

test('Replace all matches in the line', async () => {
await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '<Esc>']);
await runCmdLine('%s/a/d', modeHandler);

assertEqualLines(['dbd']);
});

test('Replace with `g` flag inverts global flag', async () => {
await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '<Esc>']);
await runCmdLine('%s/a/d/g', modeHandler);

assertEqualLines(['dba']);
});

test('Replace multiple lines', async () => {
await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '<Esc>', 'o', 'a', 'b']);
await runCmdLine('%s/a/d/', modeHandler);

assertEqualLines(['dbd', 'db']);
});

test('Replace across specific lines', async () => {
await modeHandler.handleMultipleKeyEvents(['i', 'a', 'b', 'a', '<Esc>', 'o', 'a', 'b']);
await runCmdLine('1,1s/a/d/', modeHandler);

assertEqualLines(['dbd', 'ab']);
});

test('Replace current line with no active selection', async () => {
await modeHandler.handleMultipleKeyEvents([
'i',
'a',
'b',
'a',
'<Esc>',
'o',
'a',
'b',
'<Esc>',
]);
await runCmdLine('s/a/d/', modeHandler);

assertEqualLines(['aba', 'db']);
});

test('Replace text in selection', async () => {
await modeHandler.handleMultipleKeyEvents([
'i',
'a',
'b',
'a',
'<Esc>',
'o',
'a',
'b',
'<Esc>',
'$',
'v',
'k',
'0',
]);
await runCmdLine("'<,'>s/a/d/", modeHandler);

assertEqualLines(['dbd', 'db']);
});

test('Substitute support marks', async () => {
await modeHandler.handleMultipleKeyEvents([
'i',
'a',
'b',
'c',
'<Esc>',
'y',
'y',
'2',
'p',
'g',
'g',
'm',
'a',
'j',
'm',
'b',
]);
await runCmdLine("'a,'bs/a/d/", modeHandler);

assertEqualLines(['dbc', 'dbc', 'abc']);
});
});
});