Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.
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
28 changes: 28 additions & 0 deletions src/editor/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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} column Display column number
* @return {number}
*/
Editor.prototype.getCharIndexForColumn = 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.
Expand Down
10 changes: 9 additions & 1 deletion src/editor/EditorCommandHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.getCharIndexForColumn(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});
Expand Down
25 changes: 25 additions & 0 deletions test/spec/EditorCommandHandlers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down Expand Up @@ -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";

Expand Down