Skip to content

Commit

Permalink
Merge pull request #525 from rebornix/SubstituteNoRange
Browse files Browse the repository at this point in the history
Substitute with no range or marks
  • Loading branch information
johnfn authored Aug 8, 2016
2 parents 77fb6a5 + 8042b93 commit f3dfd1d
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 26 deletions.
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"
]);
});
});

0 comments on commit f3dfd1d

Please sign in to comment.