From 2978dbe7c0991a2b3b69571a3bf5cf8ea0d5b24a Mon Sep 17 00:00:00 2001 From: Tienshiao Ma Date: Tue, 23 Feb 2016 14:22:40 -0800 Subject: [PATCH 1/6] Add E (end of WORD), and fix up e (end of word). --- README.md | 2 +- src/mode/modeNormal.ts | 1 + src/motion/motion.ts | 6 ++++ src/motion/position.ts | 69 +++++++++++++++++++++++++++--------------- test/motion.test.ts | 39 ++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index bab826ddab6..7a83aba33b2 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Status | Key | Description :white_check_mark: | w | words forward :white_check_mark: | W | N blank-separated WORDS forward :white_check_mark: | e | forward to the end of the word - | E | forward to the end of the Nth blank-separated WORD +:white_check_mark: | E | forward to the end of the Nth blank-separated WORD :white_check_mark: | b | words backward :white_check_mark: | B | N blank-separated WORDS backward | ge | backward to the end of the Nth word diff --git a/src/mode/modeNormal.ts b/src/mode/modeNormal.ts index ffe48c63f23..40e9d4d6e01 100644 --- a/src/mode/modeNormal.ts +++ b/src/mode/modeNormal.ts @@ -25,6 +25,7 @@ export class NormalMode extends Mode { "w" : async (c) => { return c.wordRight().move(); }, "W" : async (c) => { return c.bigWordRight().move(); }, "e" : async (c) => { return c.goToEndOfCurrentWord().move(); }, + "E" : async (c) => { return c.goToEndOfCurrentBigWord().move(); }, "b" : async (c) => { return c.wordLeft().move(); }, "B" : async (c) => { return c.bigWordLeft().move(); }, "}" : async (c) => { return c.goToEndOfCurrentParagraph().move(); }, diff --git a/src/motion/motion.ts b/src/motion/motion.ts index f438a21179a..ffcd1b52cc4 100644 --- a/src/motion/motion.ts +++ b/src/motion/motion.ts @@ -209,6 +209,12 @@ export class Motion implements vscode.Disposable { return this; } + public goToEndOfCurrentBigWord(): Motion { + this._position = this.position.getCurrentBigWordEnd(); + this._desiredColumn = this._position.character; + return this; + } + public goToEndOfCurrentParagraph(): Motion { this._position = this.position.getCurrentParagraphEnd(); this._desiredColumn = this.position.character; diff --git a/src/motion/position.ts b/src/motion/position.ts index 88fcb56998b..77189c4bcda 100644 --- a/src/motion/position.ts +++ b/src/motion/position.ts @@ -12,8 +12,6 @@ export enum PositionOptions { export class Position extends vscode.Position { private static NonWordCharacters = "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"; private static NonBigWordCharacters = ""; - private static WordDelimiters: string[] = ["(", ")", "[", "]", "{", "}", ":", " ", - "=", "<", ">", "|", "/", "'", "\"", "~", "`", "@", "*", "+", "-", "?", ",", ".", ";"]; private _nonWordCharRegex : RegExp; private _nonBigWordCharRegex : RegExp; @@ -95,25 +93,11 @@ export class Position extends vscode.Position { } public getCurrentWordEnd(): Position { - if (!TextEditor.isLastLine(this) && this.character === this.getLineEnd().character) { - // go to next line - let line = TextEditor.getLineAt(this.translate(1)); - return new Position(line.lineNumber, line.firstNonWhitespaceCharacterIndex, this.positionOptions); - } - - let line = TextEditor.getLineAt(this); - - if (Position.WordDelimiters.indexOf(line.text.charAt(this.character)) !== -1) { - return new Position(this.line, this.character + 1, this.positionOptions); - } - - for (var index = this.character; index < line.text.length; index++) { - if (Position.WordDelimiters.indexOf(line.text.charAt(index)) !== -1) { - return new Position(this.line, index, this.positionOptions); - } - } + return this.getCurrentWordEndWithRegex(this._nonWordCharRegex); + } - return this.getLineEnd(); + public getCurrentBigWordEnd(): Position { + return this.getCurrentWordEndWithRegex(this._nonBigWordCharRegex); } /** @@ -240,9 +224,9 @@ export class Position extends vscode.Position { } private getWordLeftWithRegex(regex: RegExp) : Position { - var workingPosition = new Position(this.line, this.character, this.positionOptions); - var currentLine = TextEditor.getLineAt(this); - var currentCharacter = this.character; + let workingPosition = new Position(this.line, this.character, this.positionOptions); + let currentLine = TextEditor.getLineAt(this); + let currentCharacter = this.character; if (!TextEditor.isFirstLine(this) && this.character <= currentLine.firstNonWhitespaceCharacterIndex) { // perform search from very end of previous line (after last character) @@ -262,7 +246,7 @@ export class Position extends vscode.Position { positions.push(result.index); } - for (var index = 0; index < positions.length; index++) { + for (let index = 0; index < positions.length; index++) { let position = positions[positions.length - 1 - index]; if (currentCharacter > position) { return new Position(workingPosition.line, position, workingPosition.positionOptions); @@ -296,7 +280,7 @@ export class Position extends vscode.Position { positions.push(result.index); } - for (var index = 0; index < positions.length; index++) { + for (let index = 0; index < positions.length; index++) { let position = positions[index]; if (this.character < position) { return new Position(this.line, position, this.positionOptions); @@ -311,4 +295,39 @@ export class Position extends vscode.Position { return new Position(line.lineNumber, line.firstNonWhitespaceCharacterIndex, this.positionOptions); } } + + private getCurrentWordEndWithRegex(regex: RegExp) : Position { + let workingPosition = new Position(this.line, this.character, this.positionOptions); + let currentLine = TextEditor.getLineAt(this); + let currentCharacter = this.character; + + if (!TextEditor.isLastLine(this) && this.character >= this.getLineEnd().character) { + // go to next line + workingPosition = new Position(this.line + 1, this.character, this.positionOptions); + currentLine = TextEditor.getLineAt(workingPosition); + currentCharacter = 0; + } + + let positions = []; + + regex.lastIndex = 0; + while (true) { + let result = regex.exec(currentLine.text); + if (result === null) { + break; + } + positions.push(result.index + result[0].length - 1); + } + + for (let index = 0; index < positions.length; index++) { + let position = positions[index]; + if (currentCharacter < position) { + return new Position(workingPosition.line, position, workingPosition.positionOptions); + } + } + + // Wasn't at end of the line (of not last line), and couldn't find a column in the middle of the string? + // We'll just go to the end of the line? + return this.getLineEnd(); + } } \ No newline at end of file diff --git a/test/motion.test.ts b/test/motion.test.ts index 94f84b0975e..c96bbde69d6 100644 --- a/test/motion.test.ts +++ b/test/motion.test.ts @@ -300,6 +300,45 @@ suite("word motion", () => { }); }); + suite("end of word right", () => { + test("move to end of current word right", () => { + let motion = new Motion(MotionMode.Caret).moveTo(0, 4).goToEndOfCurrentWord(); + assert.equal(motion.position.line, 0); + assert.equal(motion.position.character, 7); + }); + + test("move to end of next word right", () => { + let motion = new Motion(MotionMode.Caret).moveTo(0, 7).goToEndOfCurrentWord(); + assert.equal(motion.position.line, 0); + assert.equal(motion.position.character, 8); + }); + + test("end of last word should move to next line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(0, 10).goToEndOfCurrentWord(); + assert.equal(motion.position.line, 1); + assert.equal(motion.position.character, 7); + }); + }); + + suite("end of WORD right", () => { + test("move to end of current WORD right", () => { + let motion = new Motion(MotionMode.Caret).moveTo(0, 4).goToEndOfCurrentBigWord(); + assert.equal(motion.position.line, 0); + assert.equal(motion.position.character, 8); + }); + + test("move to end of next WORD right", () => { + let motion = new Motion(MotionMode.Caret).moveTo(0, 8).goToEndOfCurrentBigWord(); + assert.equal(motion.position.line, 0); + assert.equal(motion.position.character, 10); + }); + + test("end of last WORD should move to next line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(0, 10).goToEndOfCurrentBigWord(); + assert.equal(motion.position.line, 1); + assert.equal(motion.position.character, 7); + }); + }); test("line begin cursor on first non-blank character", () => { let motion = new Motion(MotionMode.Caret).moveTo(3, 3).firstLineNonBlankChar(); From 73115fd08ee524eeb2ea784209d0f6e3457cdd88 Mon Sep 17 00:00:00 2001 From: Tienshiao Ma Date: Thu, 25 Feb 2016 00:01:42 -0800 Subject: [PATCH 2/6] Fix infinite loop when w/b/e tried to traverse empty line. Traversing empty lines/whitespace only lines should match vim. --- src/motion/position.ts | 185 +++++++++++++++++++++++++---------------- test/motion.test.ts | 81 +++++++++++++++++- 2 files changed, 192 insertions(+), 74 deletions(-) diff --git a/src/motion/position.ts b/src/motion/position.ts index 77189c4bcda..d9f7e9d8203 100644 --- a/src/motion/position.ts +++ b/src/motion/position.ts @@ -224,32 +224,36 @@ export class Position extends vscode.Position { } private getWordLeftWithRegex(regex: RegExp) : Position { - let workingPosition = new Position(this.line, this.character, this.positionOptions); let currentLine = TextEditor.getLineAt(this); - let currentCharacter = this.character; - if (!TextEditor.isFirstLine(this) && this.character <= currentLine.firstNonWhitespaceCharacterIndex) { - // perform search from very end of previous line (after last character) - workingPosition = new Position(this.line - 1, this.character, this.positionOptions); - currentLine = TextEditor.getLineAt(workingPosition); - currentCharacter = workingPosition.getLineEnd().character + 1; + if (currentLine.text.length === 0) { + if (TextEditor.isFirstLine(this)) { + return this.getLineBegin(); + } else { + // perform search from very end of previous line (after last character) + let newPosition = new Position(this.line - 1, this.character, this.positionOptions); + newPosition = new Position(newPosition.line, newPosition.getLineEnd().character + 1, newPosition.positionOptions); + return newPosition.getWordLeftWithRegex(regex); + } } - let positions = []; + if (this.character > currentLine.firstNonWhitespaceCharacterIndex) { + let positions = []; - regex.lastIndex = 0; - while (true) { - let result = regex.exec(currentLine.text); - if (result === null) { - break; + regex.lastIndex = 0; + while (true) { + let result = regex.exec(currentLine.text); + if (result === null) { + break; + } + positions.push(result.index); } - positions.push(result.index); - } - for (let index = 0; index < positions.length; index++) { - let position = positions[positions.length - 1 - index]; - if (currentCharacter > position) { - return new Position(workingPosition.line, position, workingPosition.positionOptions); + for (let index = 0; index < positions.length; index++) { + let position = positions[positions.length - 1 - index]; + if (this.character > position) { + return new Position(this.line, position, this.positionOptions); + } } } @@ -257,77 +261,118 @@ export class Position extends vscode.Position { return this.getLineBegin(); } else { let prevLine = new Position(this.line - 1, 0, this.positionOptions); - return prevLine.getLineEnd(); + let line = TextEditor.getLineAt(prevLine); + if (line.text.length === 0) { + return prevLine; + } else { + // perform search from very end of previous line (after last character) + prevLine = new Position(prevLine.line, prevLine.getLineEnd().character + 1, prevLine.positionOptions); + return prevLine.getWordLeftWithRegex(regex); + } } } private getWordRightWithRegex(regex: RegExp) : Position { - if (!TextEditor.isLastLine(this) && this.character >= this.getLineEnd().character) { - // go to next line - let line = TextEditor.getLineAt(this.translate(1)); - return new Position(line.lineNumber, line.firstNonWhitespaceCharacterIndex, this.positionOptions); - } - - let currentLine = TextEditor.getLineAt(this); - let positions = []; + let currPosLine = this.line; + let currPosCharacter = this.character; // we don't use a Position because sometimes we want to a Position.character to be -1 - regex.lastIndex = 0; while (true) { - let result = regex.exec(currentLine.text); - if (result === null) { - break; + let workingPosition = new Position(currPosLine, 0, this.positionOptions); + let currLine = TextEditor.getLineAt(workingPosition); + if (currLine.text.length === 0) { + if (TextEditor.isLastLine(workingPosition)) { + return workingPosition.getLineEnd(); + } else { + currPosLine += 1; + currPosCharacter = -1; + continue; + } + } else if (currLine.isEmptyOrWhitespace) { + currPosLine += 1; + currPosCharacter = -1; + continue; } - positions.push(result.index); - } - for (let index = 0; index < positions.length; index++) { - let position = positions[index]; - if (this.character < position) { - return new Position(this.line, position, this.positionOptions); + let positions = []; + + regex.lastIndex = 0; + while (true) { + let result = regex.exec(currLine.text); + if (result === null) { + break; + } + positions.push(result.index); } - } - if (this.line === this.getDocumentEnd().line) { - return this.getLineEnd(); - } else { - // go to next line - let line = TextEditor.getLineAt(this.translate(1)); - return new Position(line.lineNumber, line.firstNonWhitespaceCharacterIndex, this.positionOptions); + for (let index = 0; index < positions.length; index++) { + let position = positions[index]; + if (currPosCharacter < position) { + return new Position(currPosLine, position, this.positionOptions); + } + } + + if (currPosLine === this.getDocumentEnd().line) { + return workingPosition.getLineEnd(); + } else { + // go to next line + let newPosition = new Position(currPosLine + 1, 0, this.positionOptions); + let line = TextEditor.getLineAt(newPosition); + if (line.text.length === 0) { + return newPosition; + } else { + currPosLine += 1; + currPosCharacter = -1; + continue; + } + } } } private getCurrentWordEndWithRegex(regex: RegExp) : Position { - let workingPosition = new Position(this.line, this.character, this.positionOptions); - let currentLine = TextEditor.getLineAt(this); - let currentCharacter = this.character; + let currPosLine = this.line; + let currPosCharacter = this.character; // we don't use a Position because sometimes we want to a Position.character to be -1 - if (!TextEditor.isLastLine(this) && this.character >= this.getLineEnd().character) { - // go to next line - workingPosition = new Position(this.line + 1, this.character, this.positionOptions); - currentLine = TextEditor.getLineAt(workingPosition); - currentCharacter = 0; - } + while (true) { + let workingPosition = new Position(currPosLine, 0, this.positionOptions); + let currLine = TextEditor.getLineAt(workingPosition); + + if (currLine.text.length === 0 || currLine.isEmptyOrWhitespace) { + if (TextEditor.isLastLine(workingPosition)) { + return workingPosition.getLineEnd(); + } else { + // go to next line + currPosLine += 1; + currPosCharacter = -1; + continue; + } + } - let positions = []; + let positions = []; - regex.lastIndex = 0; - while (true) { - let result = regex.exec(currentLine.text); - if (result === null) { - break; + regex.lastIndex = 0; + while (true) { + let result = regex.exec(currLine.text); + if (result === null) { + break; + } + positions.push(result.index + result[0].length - 1); } - positions.push(result.index + result[0].length - 1); - } - for (let index = 0; index < positions.length; index++) { - let position = positions[index]; - if (currentCharacter < position) { - return new Position(workingPosition.line, position, workingPosition.positionOptions); + for (let index = 0; index < positions.length; index++) { + let position = positions[index]; + if (currPosCharacter < position) { + return new Position(currPosLine, position, this.positionOptions); + } } - } - // Wasn't at end of the line (of not last line), and couldn't find a column in the middle of the string? - // We'll just go to the end of the line? - return this.getLineEnd(); + if (currPosLine === this.getDocumentEnd().line) { + return workingPosition.getLineEnd(); + } else { + // go to next line + currPosLine += 1; + currPosCharacter = -1; + continue; + } + } } } \ No newline at end of file diff --git a/test/motion.test.ts b/test/motion.test.ts index c96bbde69d6..833b1ff87ec 100644 --- a/test/motion.test.ts +++ b/test/motion.test.ts @@ -212,7 +212,9 @@ suite("word motion", () => { "if (true) {", " return true;", "} else {", + "", " return false;", + " ", "} // endif" ]; @@ -237,9 +239,21 @@ suite("word motion", () => { assert.equal(motion.position.character, 2); }); + test("last word should move to next line stops on empty line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(2, 7).wordRight(); + assert.equal(motion.position.line, 3); + assert.equal(motion.position.character, 0); + }); + + test("last word should move to next line skips whitespace only line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(4, 14).wordRight(); + assert.equal(motion.position.line, 6); + assert.equal(motion.position.character, 0); + }); + test("last word on last line should go to end of document (special case!)", () => { - let motion = new Motion(MotionMode.Caret).moveTo(4, 6).wordRight(); - assert.equal(motion.position.line, 4); + let motion = new Motion(MotionMode.Caret).moveTo(6, 6).wordRight(); + assert.equal(motion.position.line, 6); assert.equal(motion.position.character, 9); }); @@ -264,6 +278,17 @@ suite("word motion", () => { assert.equal(motion.position.character, 10); }); + test("first word should move to previous line, stops on empty line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(4, 2).wordLeft(); + assert.equal(motion.position.line, 3); + assert.equal(motion.position.character, 0); + }); + + test("first word should move to previous line, skips whitespace only line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(6, 0).wordLeft(); + assert.equal(motion.position.line, 4); + assert.equal(motion.position.character, 14); + }); }); suite("WORD right", () => { @@ -278,6 +303,18 @@ suite("word motion", () => { assert.equal(motion.position.line, 2); assert.equal(motion.position.character, 0); }); + + test("last WORD should move to next line stops on empty line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(2, 7).bigWordRight(); + assert.equal(motion.position.line, 3); + assert.equal(motion.position.character, 0); + }); + + test("last WORD should move to next line skips whitespace only line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(4, 12).bigWordRight(); + assert.equal(motion.position.line, 6); + assert.equal(motion.position.character, 0); + }); }); suite("WORD left", () => { @@ -298,6 +335,18 @@ suite("word motion", () => { assert.equal(motion.position.line, 1); assert.equal(motion.position.character, 9); }); + + test("first WORD should move to previous line, stops on empty line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(4, 2).bigWordLeft(); + assert.equal(motion.position.line, 3); + assert.equal(motion.position.character, 0); + }); + + test("first WORD should move to previous line, skips whitespace only line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(6, 0).bigWordLeft(); + assert.equal(motion.position.line, 4); + assert.equal(motion.position.character, 9); + }); }); suite("end of word right", () => { @@ -318,6 +367,18 @@ suite("word motion", () => { assert.equal(motion.position.line, 1); assert.equal(motion.position.character, 7); }); + + test("end of last word should move to next line skips empty line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(2, 7).goToEndOfCurrentWord(); + assert.equal(motion.position.line, 4); + assert.equal(motion.position.character, 7); + }); + + test("end of last word should move to next line skips whitespace only line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(4, 14).goToEndOfCurrentWord(); + assert.equal(motion.position.line, 6); + assert.equal(motion.position.character, 0); + }); }); suite("end of WORD right", () => { @@ -338,17 +399,29 @@ suite("word motion", () => { assert.equal(motion.position.line, 1); assert.equal(motion.position.character, 7); }); + + test("end of last WORD should move to next line skips empty line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(2, 7).goToEndOfCurrentWord(); + assert.equal(motion.position.line, 4); + assert.equal(motion.position.character, 7); + }); + + test("end of last WORD should move to next line skips whitespace only line", () => { + let motion = new Motion(MotionMode.Caret).moveTo(4, 14).goToEndOfCurrentWord(); + assert.equal(motion.position.line, 6); + assert.equal(motion.position.character, 0); + }); }); test("line begin cursor on first non-blank character", () => { - let motion = new Motion(MotionMode.Caret).moveTo(3, 3).firstLineNonBlankChar(); + let motion = new Motion(MotionMode.Caret).moveTo(4, 3).firstLineNonBlankChar(); assert.equal(motion.position.line, 0); assert.equal(motion.position.character, 0); }); test("last line begin cursor on first non-blank character", () => { let motion = new Motion(MotionMode.Caret).moveTo(0, 0).lastLineNonBlankChar(); - assert.equal(motion.position.line, 4); + assert.equal(motion.position.line, 6); assert.equal(motion.position.character, 0); }); }); From a1f610a25680fc06481068b8b24fbb987e99f569 Mon Sep 17 00:00:00 2001 From: Tienshiao Ma Date: Fri, 26 Feb 2016 09:41:56 -0800 Subject: [PATCH 3/6] Fixed bug in regex, allowing clean up of some guards --- src/motion/position.ts | 64 ++++++++---------------------------------- 1 file changed, 12 insertions(+), 52 deletions(-) diff --git a/src/motion/position.ts b/src/motion/position.ts index d9f7e9d8203..b10863679d4 100644 --- a/src/motion/position.ts +++ b/src/motion/position.ts @@ -217,7 +217,7 @@ export class Position extends vscode.Position { private makeWordRegex(characterSet: string) : RegExp { let escaped = characterSet && _.escapeRegExp(characterSet); - let segments = ["(^[\t ]*$)"]; + let segments = []; segments.push(`([^\\s${escaped}]+)`); segments.push(`[${escaped}]+`); return new RegExp(segments.join("|"), "g"); @@ -225,35 +225,21 @@ export class Position extends vscode.Position { private getWordLeftWithRegex(regex: RegExp) : Position { let currentLine = TextEditor.getLineAt(this); + let positions = []; - if (currentLine.text.length === 0) { - if (TextEditor.isFirstLine(this)) { - return this.getLineBegin(); - } else { - // perform search from very end of previous line (after last character) - let newPosition = new Position(this.line - 1, this.character, this.positionOptions); - newPosition = new Position(newPosition.line, newPosition.getLineEnd().character + 1, newPosition.positionOptions); - return newPosition.getWordLeftWithRegex(regex); + regex.lastIndex = 0; + while (true) { + let result = regex.exec(currentLine.text); + if (result === null) { + break; } + positions.push(result.index); } - if (this.character > currentLine.firstNonWhitespaceCharacterIndex) { - let positions = []; - - regex.lastIndex = 0; - while (true) { - let result = regex.exec(currentLine.text); - if (result === null) { - break; - } - positions.push(result.index); - } - - for (let index = 0; index < positions.length; index++) { - let position = positions[positions.length - 1 - index]; - if (this.character > position) { - return new Position(this.line, position, this.positionOptions); - } + for (let index = 0; index < positions.length; index++) { + let position = positions[positions.length - 1 - index]; + if (this.character > position) { + return new Position(this.line, position, this.positionOptions); } } @@ -279,20 +265,6 @@ export class Position extends vscode.Position { while (true) { let workingPosition = new Position(currPosLine, 0, this.positionOptions); let currLine = TextEditor.getLineAt(workingPosition); - if (currLine.text.length === 0) { - if (TextEditor.isLastLine(workingPosition)) { - return workingPosition.getLineEnd(); - } else { - currPosLine += 1; - currPosCharacter = -1; - continue; - } - } else if (currLine.isEmptyOrWhitespace) { - currPosLine += 1; - currPosCharacter = -1; - continue; - } - let positions = []; regex.lastIndex = 0; @@ -335,18 +307,6 @@ export class Position extends vscode.Position { while (true) { let workingPosition = new Position(currPosLine, 0, this.positionOptions); let currLine = TextEditor.getLineAt(workingPosition); - - if (currLine.text.length === 0 || currLine.isEmptyOrWhitespace) { - if (TextEditor.isLastLine(workingPosition)) { - return workingPosition.getLineEnd(); - } else { - // go to next line - currPosLine += 1; - currPosCharacter = -1; - continue; - } - } - let positions = []; regex.lastIndex = 0; From 131bf4f5b0d227ba874f5f17e981292a563e4f99 Mon Sep 17 00:00:00 2001 From: johnfn Date: Sat, 27 Feb 2016 12:27:05 -0800 Subject: [PATCH 4/6] Clean up some code --- .vscode/launch.json | 4 +-- src/motion/position.ts | 60 ++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 213356475a9..20594863246 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ // "runtimeArgs": ["--harmony-default-parameters", "--harmony-rest-parameters"], "stopOnEntry": false, "sourceMaps": true, - "outDir": "out/src", + "outDir": "${workspaceRoot}/out/src", "preLaunchTask": "npm" }, { @@ -23,7 +23,7 @@ // "runtimeArgs": ["--js-flags=\"--harmony --harmony-default-parameters\""], "stopOnEntry": false, "sourceMaps": true, - "outDir": "out/test", + "outDir": "${workspaceRoot}/out/test", "preLaunchTask": "npm" } ] diff --git a/src/motion/position.ts b/src/motion/position.ts index b10863679d4..79cca71bb6d 100644 --- a/src/motion/position.ts +++ b/src/motion/position.ts @@ -220,7 +220,10 @@ export class Position extends vscode.Position { let segments = []; segments.push(`([^\\s${escaped}]+)`); segments.push(`[${escaped}]+`); - return new RegExp(segments.join("|"), "g"); + segments.push(`$^`); + let result = new RegExp(segments.join("|"), "g"); + + return result; } private getWordLeftWithRegex(regex: RegExp) : Position { @@ -258,46 +261,33 @@ export class Position extends vscode.Position { } } - private getWordRightWithRegex(regex: RegExp) : Position { - let currPosLine = this.line; - let currPosCharacter = this.character; // we don't use a Position because sometimes we want to a Position.character to be -1 + private getAllPositions(line: string, regex: RegExp): number[] { + let positions: number[] = []; + let result = regex.exec(line); - while (true) { - let workingPosition = new Position(currPosLine, 0, this.positionOptions); - let currLine = TextEditor.getLineAt(workingPosition); - let positions = []; + while (result) { + positions.push(result.index); - regex.lastIndex = 0; - while (true) { - let result = regex.exec(currLine.text); - if (result === null) { - break; - } - positions.push(result.index); - } + // Handles the case where an empty string match causes lastIndex not to advance, + // which gets us in an infinite loop. + if (result.index === regex.lastIndex) { regex.lastIndex++; } + result = regex.exec(line); + } - for (let index = 0; index < positions.length; index++) { - let position = positions[index]; - if (currPosCharacter < position) { - return new Position(currPosLine, position, this.positionOptions); - } - } + return positions; + } - if (currPosLine === this.getDocumentEnd().line) { - return workingPosition.getLineEnd(); - } else { - // go to next line - let newPosition = new Position(currPosLine + 1, 0, this.positionOptions); - let line = TextEditor.getLineAt(newPosition); - if (line.text.length === 0) { - return newPosition; - } else { - currPosLine += 1; - currPosCharacter = -1; - continue; - } + private getWordRightWithRegex(regex: RegExp): Position { + for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { + let positions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); + let newCharacter = _.find(positions, index => index > this.character || currentLine != this.line); + + if (newCharacter !== undefined) { + return new Position(currentLine, newCharacter, this.positionOptions); } } + + return new Position(TextEditor.getLineCount() - 1, 0, this.positionOptions).getLineEnd(); } private getCurrentWordEndWithRegex(regex: RegExp) : Position { From 2daac48b01e5f9a2cc70d5df3a889cc2f89772b2 Mon Sep 17 00:00:00 2001 From: Tienshiao Ma Date: Mon, 29 Feb 2016 15:32:52 -0800 Subject: [PATCH 5/6] Clean up getWordLeftWithRegex() and getCurrentWordEndWithRegex() to use similar style as getWordRightWithRegex(). --- src/motion/position.ts | 95 +++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 61 deletions(-) diff --git a/src/motion/position.ts b/src/motion/position.ts index 79cca71bb6d..4e7953d6fd4 100644 --- a/src/motion/position.ts +++ b/src/motion/position.ts @@ -226,47 +226,30 @@ export class Position extends vscode.Position { return result; } - private getWordLeftWithRegex(regex: RegExp) : Position { - let currentLine = TextEditor.getLineAt(this); - let positions = []; - - regex.lastIndex = 0; - while (true) { - let result = regex.exec(currentLine.text); - if (result === null) { - break; - } + private getAllPositions(line: string, regex: RegExp): number[] { + let positions: number[] = []; + let result = regex.exec(line); + + while (result) { positions.push(result.index); - } - for (let index = 0; index < positions.length; index++) { - let position = positions[positions.length - 1 - index]; - if (this.character > position) { - return new Position(this.line, position, this.positionOptions); - } + // Handles the case where an empty string match causes lastIndex not to advance, + // which gets us in an infinite loop. + if (result.index === regex.lastIndex) { regex.lastIndex++; } + result = regex.exec(line); } - if (this.line === 0) { - return this.getLineBegin(); - } else { - let prevLine = new Position(this.line - 1, 0, this.positionOptions); - let line = TextEditor.getLineAt(prevLine); - if (line.text.length === 0) { - return prevLine; - } else { - // perform search from very end of previous line (after last character) - prevLine = new Position(prevLine.line, prevLine.getLineEnd().character + 1, prevLine.positionOptions); - return prevLine.getWordLeftWithRegex(regex); - } - } + return positions; } - private getAllPositions(line: string, regex: RegExp): number[] { + private getAllEndPositions(line: string, regex: RegExp): number[] { let positions: number[] = []; let result = regex.exec(line); while (result) { - positions.push(result.index); + if (result[0].length) { + positions.push(result.index + result[0].length - 1); + } // Handles the case where an empty string match causes lastIndex not to advance, // which gets us in an infinite loop. @@ -277,6 +260,19 @@ export class Position extends vscode.Position { return positions; } + private getWordLeftWithRegex(regex: RegExp) : Position { + for (let currentLine = this.line; currentLine >= 0; currentLine--) { + let positions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); + let newCharacter = _.find(positions.reverse(), index => index < this.character || currentLine != this.line); + + if (newCharacter !== undefined) { + return new Position(currentLine, newCharacter, this.positionOptions); + } + } + + return new Position(0, 0, this.positionOptions).getLineBegin(); + } + private getWordRightWithRegex(regex: RegExp): Position { for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { let positions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); @@ -291,38 +287,15 @@ export class Position extends vscode.Position { } private getCurrentWordEndWithRegex(regex: RegExp) : Position { - let currPosLine = this.line; - let currPosCharacter = this.character; // we don't use a Position because sometimes we want to a Position.character to be -1 - - while (true) { - let workingPosition = new Position(currPosLine, 0, this.positionOptions); - let currLine = TextEditor.getLineAt(workingPosition); - let positions = []; - - regex.lastIndex = 0; - while (true) { - let result = regex.exec(currLine.text); - if (result === null) { - break; - } - positions.push(result.index + result[0].length - 1); - } - - for (let index = 0; index < positions.length; index++) { - let position = positions[index]; - if (currPosCharacter < position) { - return new Position(currPosLine, position, this.positionOptions); - } - } + for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { + let positions = this.getAllEndPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); + let newCharacter = _.find(positions, index => index > this.character || currentLine != this.line); - if (currPosLine === this.getDocumentEnd().line) { - return workingPosition.getLineEnd(); - } else { - // go to next line - currPosLine += 1; - currPosCharacter = -1; - continue; + if (newCharacter !== undefined) { + return new Position(currentLine, newCharacter, this.positionOptions); } } + + return new Position(TextEditor.getLineCount() - 1, 0, this.positionOptions).getLineEnd(); } } \ No newline at end of file From 27168200fa89cf4a44e6d2bb2dd2e87dfa2eebfd Mon Sep 17 00:00:00 2001 From: Tienshiao Ma Date: Mon, 29 Feb 2016 15:33:57 -0800 Subject: [PATCH 6/6] Fix linter errors. --- src/motion/position.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/motion/position.ts b/src/motion/position.ts index 4e7953d6fd4..a3655147cc1 100644 --- a/src/motion/position.ts +++ b/src/motion/position.ts @@ -263,7 +263,7 @@ export class Position extends vscode.Position { private getWordLeftWithRegex(regex: RegExp) : Position { for (let currentLine = this.line; currentLine >= 0; currentLine--) { let positions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(positions.reverse(), index => index < this.character || currentLine != this.line); + let newCharacter = _.find(positions.reverse(), index => index < this.character || currentLine !== this.line); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter, this.positionOptions); @@ -276,7 +276,7 @@ export class Position extends vscode.Position { private getWordRightWithRegex(regex: RegExp): Position { for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { let positions = this.getAllPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(positions, index => index > this.character || currentLine != this.line); + let newCharacter = _.find(positions, index => index > this.character || currentLine !== this.line); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter, this.positionOptions); @@ -289,7 +289,7 @@ export class Position extends vscode.Position { private getCurrentWordEndWithRegex(regex: RegExp) : Position { for (let currentLine = this.line; currentLine < TextEditor.getLineCount(); currentLine++) { let positions = this.getAllEndPositions(TextEditor.getLineAt(new vscode.Position(currentLine, 0)).text, regex); - let newCharacter = _.find(positions, index => index > this.character || currentLine != this.line); + let newCharacter = _.find(positions, index => index > this.character || currentLine !== this.line); if (newCharacter !== undefined) { return new Position(currentLine, newCharacter, this.positionOptions);