From f7a27f3292985cc796612a30fbd7dc108dffd33c Mon Sep 17 00:00:00 2001 From: Jason Poon Date: Tue, 1 Dec 2015 11:57:38 -0800 Subject: [PATCH] Add cursor and textEditor tests. Fixed a couple of bugs --- src/cursor.ts | 50 ++++++----- src/mode/modeInsert.ts | 2 +- src/mode/modeNormal.ts | 16 ++-- src/textEditor.ts | 13 +-- test/cursor.test.ts | 186 ++++++++++++++++++++++++++++++++++++++++ test/index.ts | 4 +- test/textEditor.test.ts | 69 +++++++++++++++ 7 files changed, 304 insertions(+), 36 deletions(-) create mode 100644 test/cursor.test.ts create mode 100644 test/textEditor.test.ts diff --git a/src/cursor.ts b/src/cursor.ts index 9d0f513d26e..79b3c580e23 100644 --- a/src/cursor.ts +++ b/src/cursor.ts @@ -4,8 +4,14 @@ import TextEditor from "./textEditor"; export default class Cursor { private static prevColumn: number = 0; - static move(position: vscode.Position) { - const newSelection = new vscode.Selection(position, position); + static move(newPosition: vscode.Position) { + let curPosition = this.currentPosition(); + + if (newPosition.line === curPosition.line) { + this.prevColumn = newPosition.character; + } + + const newSelection = new vscode.Selection(newPosition, newPosition); vscode.window.activeTextEditor.selection = newSelection; } @@ -20,8 +26,7 @@ export default class Cursor { if (column > 0) { column--; } - - this.prevColumn = column; + return new vscode.Position(pos.line, column); } @@ -29,11 +34,10 @@ export default class Cursor { let pos = this.currentPosition(); let column = pos.character; - if (column < TextEditor.ReadLine(pos.line).length - 1) { + if (column < TextEditor.readLine(pos.line).length - 1) { column++; } - - this.prevColumn = column; + return new vscode.Position(pos.line, column); } @@ -43,7 +47,7 @@ export default class Cursor { let column = this.prevColumn; if (!Cursor.isLastLine(line)) { - let nextLineMaxColumn = TextEditor.ReadLine(++line).length - 1; + let nextLineMaxColumn = TextEditor.readLine(++line).length - 1; if (nextLineMaxColumn < 0) { nextLineMaxColumn = 0; @@ -63,7 +67,7 @@ export default class Cursor { let column = this.prevColumn; if (line !== 0) { - let nextLineMaxColumn = TextEditor.ReadLine(--line).length - 1; + let nextLineMaxColumn = TextEditor.readLine(--line).length - 1; if (nextLineMaxColumn < 0) { nextLineMaxColumn = 0; @@ -84,23 +88,27 @@ export default class Cursor { static lineEnd() : vscode.Position { let pos = this.currentPosition(); - const lineLength = TextEditor.ReadLine(pos.line).length; + const lineLength = TextEditor.readLine(pos.line).length; return new vscode.Position(pos.line, lineLength); - } - - private static isLastLine(line: number): boolean { - return (vscode.window.activeTextEditor.document.lineCount - 1) === line; } - static checkLineEnd() : void { - let pos = this.currentPosition(); - const lineLength = TextEditor.ReadLine(pos.line).length; - if (pos.character === 0 || lineLength === 0) { - return; - } else if (pos.character >= lineLength) { - this.move(pos.translate(0, -1)); + static documentBegin() : vscode.Position { + return new vscode.Position(0, 0); + } + + static documentEnd() : vscode.Position { + let line = vscode.window.activeTextEditor.document.lineCount - 1; + if (line < 0) { + line = 0; } + + let column = TextEditor.readLine(line).length; + return new vscode.Position(line, column); + } + + private static isLastLine(line: number): boolean { + return (vscode.window.activeTextEditor.document.lineCount - 1) === line; } } diff --git a/src/mode/modeInsert.ts b/src/mode/modeInsert.ts index 5343e9a36c7..8e3a3bb315b 100644 --- a/src/mode/modeInsert.ts +++ b/src/mode/modeInsert.ts @@ -44,7 +44,7 @@ export default class InsertMode extends Mode { HandleKeyEvent(key : string) : void { this.keyHistory.push(key); - TextEditor.Insert(this.ResolveKeyValue(key)); + TextEditor.insert(this.ResolveKeyValue(key)); vscode.commands.executeCommand("editor.action.triggerSuggest"); } diff --git a/src/mode/modeNormal.ts b/src/mode/modeNormal.ts index 26de802bf83..883031e127b 100644 --- a/src/mode/modeNormal.ts +++ b/src/mode/modeNormal.ts @@ -29,7 +29,7 @@ export default class CommandMode extends Mode { "dw" : () => { vscode.commands.executeCommand("deleteWordRight"); }, "db" : () => { vscode.commands.executeCommand("deleteWordLeft"); }, "esc": () => { vscode.commands.executeCommand("workbench.action.closeMessages"); }, - "x" : () => { this.CommandDelete(1); } + "x" : () => { this.CommandDelete(1); } }; } @@ -64,11 +64,15 @@ export default class CommandMode extends Mode { } private CommandDelete(n: number) : void { - var pos : vscode.Position = Cursor.currentPosition(); - var end : vscode.Position = pos.translate(0, n); - var range : vscode.Range = new vscode.Range(pos, end); - TextEditor.Delete(range).then(function() { - Cursor.checkLineEnd(); + let pos = Cursor.currentPosition(); + let end = pos.translate(0, n); + let range : vscode.Range = new vscode.Range(pos, end); + TextEditor.delete(range).then(function() { + let lineEnd = Cursor.lineEnd(); + + if (pos.character === lineEnd.character) { + Cursor.move(Cursor.left()); + } }); } } \ No newline at end of file diff --git a/src/textEditor.ts b/src/textEditor.ts index 2fe64b64c7c..ce6754dd8d9 100644 --- a/src/textEditor.ts +++ b/src/textEditor.ts @@ -1,7 +1,7 @@ import * as vscode from "vscode"; export default class TextEditor { - static Insert(text: string, position: vscode.Position = null) : Thenable { + static insert(text: string, position: vscode.Position = null) : Thenable { if (position === null) { position = vscode.window.activeTextEditor.selection.active; } @@ -11,27 +11,28 @@ export default class TextEditor { }); } - static Delete(range: vscode.Range) : Thenable { + static delete(range: vscode.Range) : Thenable { return vscode.window.activeTextEditor.edit((editBuilder) => { editBuilder.delete(range); }); } + - static Replace(range: vscode.Range, text: string) : Thenable { + static replace(range: vscode.Range, text: string) : Thenable { return vscode.window.activeTextEditor.edit((editBuilder) => { editBuilder.replace(range, text); }); } - static ReadLine(lineNo: number = null): string { + static readLine(lineNo: number = null): string { if (lineNo === null) { lineNo = vscode.window.activeTextEditor.selection.active.line; } - if (vscode.window.activeTextEditor.document.lineCount < lineNo) { + if (lineNo >= vscode.window.activeTextEditor.document.lineCount) { throw new RangeError(); } - + return vscode.window.activeTextEditor.document.lineAt(lineNo).text; } } diff --git a/test/cursor.test.ts b/test/cursor.test.ts new file mode 100644 index 00000000000..381c420cdbd --- /dev/null +++ b/test/cursor.test.ts @@ -0,0 +1,186 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import TextEditor from './../src/textEditor'; +import Cursor from './../src/cursor'; + +suite("cursor", () => { + let text : Array = [ + "mary had", + "a", + "little lamb" + ]; + + setup(done => { + TextEditor.insert(text.join('\n')).then(() => done()); + }); + + teardown(done => { + let range = new vscode.Range(Cursor.documentBegin(), Cursor.documentEnd()); + TextEditor.delete(range).then(() => done()); + }); + + test("left should move cursor one column left", () => { + Cursor.move(new vscode.Position(0, 5)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 0); + assert.equal(current.character, 5); + + let left = Cursor.left(); + assert.equal(left.line, 0); + assert.equal(left.character, 4); + }); + + test("left on left-most column should stay at the same location", () => { + Cursor.move(new vscode.Position(0, 0)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 0); + assert.equal(current.character, 0); + + let left = Cursor.left(); + assert.equal(left.line, 0); + assert.equal(left.character, 0); + }); + + test("right should move cursor one column right", () => { + Cursor.move(new vscode.Position(0, 5)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 0); + assert.equal(current.character, 5); + + let right = Cursor.right(); + assert.equal(right.line, 0); + assert.equal(right.character, 6); + }); + + test("right on right-most column should stay at the same location", () => { + Cursor.move(new vscode.Position(0, 7)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 0); + assert.equal(current.character, 7); + + let right = Cursor.right(); + assert.equal(right.line, 0); + assert.equal(right.character, 7); + }); + + test("down should move cursor one line down", () => { + Cursor.move(new vscode.Position(1, 0)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 1); + assert.equal(current.character, 0); + + let down = Cursor.down(); + console.log(down.character); + assert.equal(down.line, 2); + assert.equal(down.character, 0); + }); + + test("down on bottom-most line should stay at the same location", () => { + Cursor.move(new vscode.Position(2, 0)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 2); + assert.equal(current.character, 0); + + let down = Cursor.down(); + assert.equal(down.line, 2); + assert.equal(down.character, 0); + }); + + test("up should move cursor one line up", () => { + Cursor.move(new vscode.Position(1, 0)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 1); + assert.equal(current.character, 0); + + let up = Cursor.up(); + assert.equal(up.line, 0); + assert.equal(up.character, 0); + }); + + test("up on top-most line should stay at the same location", () => { + Cursor.move(new vscode.Position(0, 0)); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 0); + assert.equal(current.character, 0); + + let up = Cursor.up(); + assert.equal(up.line, 0); + assert.equal(up.character, 0); + }); + + test("keep same column as up/down", () => { + Cursor.move(new vscode.Position(0, 0)); + Cursor.move(Cursor.right()); + Cursor.move(Cursor.right()); + + let current = Cursor.currentPosition(); + assert.equal(current.line, 0); + assert.equal(current.character, 2); + + Cursor.move(Cursor.down()); + + current = Cursor.currentPosition(); + assert.equal(current.line, 1); + assert.equal(current.character, 0); + + Cursor.move(Cursor.down()); + + current = Cursor.currentPosition(); + assert.equal(current.line, 2); + assert.equal(current.character, 2); + }); + + test("get line begin cursor", () => { + Cursor.move(new vscode.Position(0, 0)); + + let pos = Cursor.lineBegin(); + + assert.equal(pos.line, 0); + assert.equal(pos.character, 0); + + Cursor.move(Cursor.down()); + + pos = Cursor.lineBegin(); + + assert.equal(pos.line, 1); + assert.equal(pos.character, 0); + }); + + test("get line end cursor", () => { + Cursor.move(new vscode.Position(0, 0)); + + let pos = Cursor.lineEnd(); + + assert.equal(pos.line, 0); + assert.equal(pos.character, text[0].length); + + Cursor.move(Cursor.down()); + + pos = Cursor.lineEnd(); + + assert.equal(pos.line, 1); + assert.equal(pos.character, text[1].length); + }); + + test("get document begin cursor", () => { + var cursor = Cursor.documentBegin(); + + assert.equal(cursor.line, 0); + assert.equal(cursor.character, 0); + }); + + test("get document end cursor", () => { + var cursor = Cursor.documentEnd(); + + assert.equal(cursor.line, 2); + assert.equal(cursor.character, text[2].length); + }); +}); diff --git a/test/index.ts b/test/index.ts index 5e827bb072b..f94c09903cf 100644 --- a/test/index.ts +++ b/test/index.ts @@ -15,8 +15,8 @@ var testRunner = require('vscode/lib/testrunner'); // You can directly control Mocha options by uncommenting the following lines // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info testRunner.configure({ - ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) - useColors: true // colored output from test results + ui: 'tdd', + useColors: true }); module.exports = testRunner; diff --git a/test/textEditor.test.ts b/test/textEditor.test.ts new file mode 100644 index 00000000000..8ef25412998 --- /dev/null +++ b/test/textEditor.test.ts @@ -0,0 +1,69 @@ +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import TextEditor from './../src/textEditor'; +import Cursor from './../src/cursor'; + +suite("text editor", () => { + suiteSetup(done => { + let range = new vscode.Range(Cursor.documentBegin(), Cursor.documentEnd()); + TextEditor.delete(range).then(done()); + }); + + suiteTeardown(done => { + var range = new vscode.Range(Cursor.documentBegin(), Cursor.documentEnd()); + TextEditor.delete(range).then(done()); + }); + + test("insert 'Hello World'", done => { + let expectedText = "Hello World"; + + TextEditor.insert(expectedText).then(x => { + assert.equal(vscode.window.activeTextEditor.document.lineCount, 1); + let actualText = TextEditor.readLine(0); + assert.equal(actualText, expectedText); + }).then(done, done); + }); + + test("replace 'World' with 'Foo Bar'", done => { + let newText = "Foo Bar"; + let start = new vscode.Position(0, 6); + let end = new vscode.Position(0, 11); + let range : vscode.Range = new vscode.Range(start, end); + + TextEditor.replace(range, newText).then( x => { + assert.equal(vscode.window.activeTextEditor.document.lineCount, 1); + + let actualText = TextEditor.readLine(0); + assert.equal(actualText, "Hello Foo Bar"); + }).then(done, done); + }); + + test("delete `Hello`", done => { + assert.equal(vscode.window.activeTextEditor.document.lineCount, 1); + + var end = new vscode.Position(0, 5); + var range = new vscode.Range(Cursor.documentBegin(), end); + + TextEditor.delete(range).then( x => { + let actualText = TextEditor.readLine(0); + assert.equal(actualText, " Foo Bar"); + }).then(done, done); + }); + + test("delete the whole line", done => { + assert.equal(vscode.window.activeTextEditor.document.lineCount, 1); + + var range = vscode.window.activeTextEditor.document.lineAt(0).range; + + TextEditor.delete(range).then( x => { + let actualText = TextEditor.readLine(0); + assert.equal(actualText, ""); + }).then(done, done); + }); + + test("try to read lines that don't exist", () => { + assert.equal(vscode.window.activeTextEditor.document.lineCount, 1); + assert.throws(() => TextEditor.readLine(1), RangeError); + assert.throws(() => TextEditor.readLine(2), RangeError); + }); +});