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 with no range or marks #525

Merged
merged 3 commits into from
Aug 8, 2016
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
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ Status | Command | Description
:white_check_mark: | :1234: ={motion} | filter the lines that are moved over through 'equalprg'
| :1234: == | filter N lines through 'equalprg'
:white_check_mark: | {visual}= | filter the highlighted lines through 'equalprg'
| :[range]s[ubstitute]/{pattern}/{string}/[g][c] | substitute {pattern} by {string} in [range] lines; with [g], replace all occurrences of {pattern}; with [c], confirm each replacement
:warning: | :[range]s[ubstitute]/{pattern}/{string}/[g][c] | substitute {pattern} by {string} in [range] lines; with [g], replace all occurrences of {pattern}; with [c], confirm each replacement
| :[range]s[ubstitute] [g][c] | repeat previous ":s" with new range and options
| & | Repeat previous ":s" on current line without options
:arrow_down: | :[range]ret[ab][!] [tabstop] | set 'tabstop' to new value and adjust white space accordingly
Expand Down
8 changes: 7 additions & 1 deletion src/actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1167,12 +1167,18 @@ export class PutBeforeWithIndentCommand extends BaseCommand {

@RegisterAction
class CommandShowCommandLine extends BaseCommand {
modes = [ModeName.Normal];
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine];
keys = [":"];

public async exec(position: Position, vimState: VimState): Promise<VimState> {
vimState.commandAction = VimSpecialCommands.ShowCommandLine;

if (vimState.currentMode === ModeName.Normal) {
vimState.commandInitialText = "";
} else {
vimState.commandInitialText = "'<,'>";
}

return vimState;
}
}
Expand Down
53 changes: 32 additions & 21 deletions src/cmd_line/commands/substitute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { TextEditor } from "../../textEditor";
export interface ISubstituteCommandArguments extends node.ICommandArgs {
pattern: string;
replace: string;
flags?: number;
flags: number;
count?: number;
}

Expand Down Expand Up @@ -60,8 +60,35 @@ export class SubstituteCommand extends node.CommandBase {
return this._arguments;
}

execute() : void {
throw new Error("not implemented");
getRegex(args: ISubstituteCommandArguments) {
let jsRegexFlags = "";

if (args.flags & SubstituteFlags.ReplaceAll) {
jsRegexFlags += "g";
}

if (args.flags & SubstituteFlags.IgnoreCase) {
jsRegexFlags += "i";
}

return new RegExp(args.pattern, jsRegexFlags);
}

async replaceTextAtLine(line: number, regex: RegExp) {
const originalContent = TextEditor.readLineAt(line);
const newContent = originalContent.replace(regex, this._arguments.replace);

if (originalContent !== newContent) {
await TextEditor.replace(new vscode.Range(line, 0, line, originalContent.length), newContent);
}
}

async execute(): Promise<void> {
const regex = this.getRegex(this._arguments);
const selection = vscode.window.activeTextEditor.selection;
const line = selection.start.isBefore(selection.end) ? selection.start.line : selection.end.line;

await this.replaceTextAtLine(line, regex);
}

async executeWithRange(modeHandler : ModeHandler, range: node.LineRange) {
Expand All @@ -84,25 +111,9 @@ export class SubstituteCommand extends node.CommandBase {
// TODO: Global Setting.
// TODO: There are differencies between Vim Regex and JS Regex.

let jsRegexFlags = "";
let flags = this._arguments.flags;

if (flags & SubstituteFlags.ReplaceAll) {
jsRegexFlags += "g";
}

if (flags & SubstituteFlags.IgnoreCase) {
jsRegexFlags += "i";
}

var regex = new RegExp(this._arguments.pattern, jsRegexFlags);
let regex = this.getRegex(this._arguments);
for (let currentLine = startLine.line; currentLine <= endLine.line && currentLine < TextEditor.getLineCount(); currentLine++) {
let originalContent = TextEditor.readLineAt(currentLine);
let content = originalContent.replace(regex, this._arguments.replace);

if (originalContent !== content) {
await TextEditor.replace(new vscode.Range(currentLine, 0, currentLine, originalContent.length), content);
}
await this.replaceTextAtLine(currentLine, regex);
}
}
}
24 changes: 24 additions & 0 deletions src/cmd_line/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ module LexerFunctions {
case "-":
tokens.push(emitToken(TokenType.Minus, state)!);
continue;
case "'":
return lexMark;
default:
return lexCommand;
}
Expand All @@ -76,6 +78,28 @@ module LexerFunctions {
return null;
}

function lexMark(state: Scanner, tokens: Token[]): ILexFunction | null {
// The first token has already been lexed.
while (true) {
if (state.isAtEof) {
return null;
}

var c = state.next();
switch (c) {
case '<':
tokens.push(emitToken(TokenType.SelectionFirstLine, state)!);
break;
case '>':
tokens.push(emitToken(TokenType.SelectionLastLine, state)!);
break;
default:
state.backup();
return lexRange;
}
}
}

function lexLineRef(state : Scanner, tokens: Token[]): ILexFunction | null {
// The first digit has already been lexed.
while (true) {
Expand Down
6 changes: 6 additions & 0 deletions src/cmd_line/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ export class LineRange {
line = Math.max(0, line - 1);
line = Math.min(doc.document.lineCount, line);
return new vscode.Position(line, 0);
case token.TokenType.SelectionFirstLine:
let start = doc.selection.start.isBeforeOrEqual(doc.selection.end) ? doc.selection.start : doc.selection.end;
return new vscode.Position(start.line, 0);
case token.TokenType.SelectionLastLine:
let end = doc.selection.start.isAfter(doc.selection.end) ? doc.selection.start : doc.selection.end;
return new vscode.Position(end.line, 0);
default:
throw new Error("not implemented");
}
Expand Down
2 changes: 2 additions & 0 deletions src/cmd_line/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ function parseLineRange(state: ParserState, commandLine: node.CommandLine): IPar
case token.TokenType.Percent:
case token.TokenType.Comma:
case token.TokenType.LineNumber:
case token.TokenType.SelectionFirstLine:
case token.TokenType.SelectionLastLine:
commandLine.range.addToken(tok);
continue;
case token.TokenType.CommandName:
Expand Down
3 changes: 2 additions & 1 deletion src/cmd_line/subparsers/substitute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export function parseSubstituteCommandArgs(args : string) : node.SubstituteComma
// TODO(rebornix): Can this ever happen?
return new node.SubstituteCommand({
pattern: "",
replace: ""
replace: "",
flags: node.SubstituteFlags.None
});
}
8 changes: 7 additions & 1 deletion src/cmd_line/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ export enum TokenType {
CommandArgs,
ForwardSearch,
ReverseSearch,
Offset
Offset,
/**
* Marks
*
*/
SelectionFirstLine,
SelectionLastLine
}

export class Token {
Expand Down
4 changes: 3 additions & 1 deletion src/mode/modeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ export class VimState {
*/
public commandAction = VimSpecialCommands.Nothing;

public commandInitialText = "";

public recordedState = new RecordedState();

/**
Expand Down Expand Up @@ -805,7 +807,7 @@ export class ModeHandler implements vscode.Disposable {

switch (command) {
case VimSpecialCommands.ShowCommandLine:
await showCmdLine("", this);
await showCmdLine(vimState.commandInitialText, this);
break;
case VimSpecialCommands.Dot:
if (!vimState.previousFullAction) {
Expand Down
18 changes: 18 additions & 0 deletions test/cmd_line/substitute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,23 @@ suite("Basic substitute", () => {
]);
});

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

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

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

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