From acf0c47e387387478417dc9a56628a30d658e089 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Mon, 24 Mar 2014 09:38:53 -0700 Subject: [PATCH 1/3] for add new selection, use display column --- src/editor/Editor.js | 28 ++++++++++++++++++++++++++++ src/editor/EditorCommandHandlers.js | 10 +++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/editor/Editor.js b/src/editor/Editor.js index dbd95186b14..ae2a61da745 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -905,6 +905,34 @@ define(function (require, exports, module) { return column; }; + /** + * Returns the string-based pos for a given display column (zero-based) in given line. Differs from column + * only when the line contains preceding \t chars. Result depends on the current tab size setting. + * @param {number} lineNum Line number + * @param {number} colum Display column number + * @return {number} + */ + Editor.prototype.getPosOffset = function (lineNum, column) { + var line = this._codeMirror.getLine(lineNum), + tabSize = null, + iCol = 0, + i; + + for (i = 0; iCol < column; i++) { + if (line[i] === '\t') { + if (tabSize === null) { + tabSize = Editor.getTabSize(); + } + if (tabSize > 0) { + iCol += (tabSize - (iCol % tabSize)); + } + } else { + iCol++; + } + } + return i; + }; + /** * Sets the cursor position within the editor. Removes any selection. * @param {number} line The 0 based line number. diff --git a/src/editor/EditorCommandHandlers.js b/src/editor/EditorCommandHandlers.js index 6c9c84aca52..cd2115dae37 100644 --- a/src/editor/EditorCommandHandlers.js +++ b/src/editor/EditorCommandHandlers.js @@ -979,14 +979,22 @@ define(function (require, exports, module) { var origSels = editor.getSelections(), newSels = []; _.each(origSels, function (sel) { - var pos; + var pos, colOffset; if ((dir === -1 && sel.start.line > editor.getFirstVisibleLine()) || (dir === 1 && sel.end.line < editor.getLastVisibleLine())) { // Add a new cursor on the next line up/down. It's okay if it overlaps another selection, because CM // will take care of throwing it away in that case. It will also take care of clipping the char position // to the end of the new line if the line is shorter. pos = _.clone(dir === -1 ? sel.start : sel.end); + + // get sel column of current selection + colOffset = editor.getColOffset(pos); + pos.line += dir; + // translate column to ch in line of new selection + pos.ch = editor.getPosOffset(pos.line, colOffset); + + // If this is the primary selection, we want the new cursor we're adding to become the // primary selection. newSels.push({start: pos, end: pos, primary: sel.primary}); From ed0cf7e31176c200ca0d94a180b248a21386abf9 Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 25 Mar 2014 15:50:49 -0700 Subject: [PATCH 2/3] changes for code review --- src/editor/Editor.js | 4 ++-- src/editor/EditorCommandHandlers.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/editor/Editor.js b/src/editor/Editor.js index d4193ca4a0f..0e044e02cfa 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -909,10 +909,10 @@ define(function (require, exports, module) { * Returns the string-based pos for a given display column (zero-based) in given line. Differs from column * only when the line contains preceding \t chars. Result depends on the current tab size setting. * @param {number} lineNum Line number - * @param {number} colum Display column number + * @param {number} column Display column number * @return {number} */ - Editor.prototype.getPosOffset = function (lineNum, column) { + Editor.prototype.getCharIndexForColumn = function (lineNum, column) { var line = this._codeMirror.getLine(lineNum), tabSize = null, iCol = 0, diff --git a/src/editor/EditorCommandHandlers.js b/src/editor/EditorCommandHandlers.js index cd2115dae37..a20657584af 100644 --- a/src/editor/EditorCommandHandlers.js +++ b/src/editor/EditorCommandHandlers.js @@ -992,7 +992,7 @@ define(function (require, exports, module) { pos.line += dir; // translate column to ch in line of new selection - pos.ch = editor.getPosOffset(pos.line, colOffset); + pos.ch = editor.getCharIndexForColumn(pos.line, colOffset); // If this is the primary selection, we want the new cursor we're adding to become the From 8c8a627f809028c0b82ac2752615b782a637861f Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 25 Mar 2014 20:58:59 -0700 Subject: [PATCH 3/3] unit tests --- test/spec/EditorCommandHandlers-test.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/spec/EditorCommandHandlers-test.js b/test/spec/EditorCommandHandlers-test.js index 3c9d8174556..97c8f8487d2 100644 --- a/test/spec/EditorCommandHandlers-test.js +++ b/test/spec/EditorCommandHandlers-test.js @@ -47,6 +47,11 @@ define(function (require, exports, module) { "\n" + "}"; + var tabbedContent = "function funcWithTabs() {\n" + + " var i = 0;\n" + + " var offset = 0;\n" + + "}"; + var myDocument, myEditor; var testPath = SpecRunnerUtils.getTestPath("/spec/EditorCommandHandlers-test-files"), @@ -3316,6 +3321,26 @@ define(function (require, exports, module) { }); + describe("Add Line to Selection with Tabs", function () { + beforeEach(function () { + setupFullEditor(tabbedContent); + }); + + it("should add a cursor on the next line before a single cursor in same visual position", function () { + myEditor.setSelection({line: 1, ch: 8}, {line: 1, ch: 8}); + CommandManager.execute(Commands.EDIT_ADD_NEXT_LINE_TO_SEL, myEditor); + expectSelections([{start: {line: 1, ch: 8}, end: {line: 1, ch: 8}, primary: false, reversed: false}, + {start: {line: 2, ch: 12}, end: {line: 2, ch: 12}, primary: true, reversed: false}]); + }); + + it("should add a cursor on the previous line before a single cursor selection in same visual position", function () { + myEditor.setSelection({line: 2, ch: 12}, {line: 2, ch: 12}); + CommandManager.execute(Commands.EDIT_ADD_PREV_LINE_TO_SEL, myEditor); + expectSelections([{start: {line: 1, ch: 8}, end: {line: 1, ch: 8}, primary: true, reversed: false}, + {start: {line: 2, ch: 12}, end: {line: 2, ch: 12}, primary: false, reversed: false}]); + }); + }); + describe("EditorCommandHandlers Integration", function () { this.category = "integration";