Skip to content

Commit

Permalink
fix #16587. Enrich Join lines with multi cursor support
Browse files Browse the repository at this point in the history
  • Loading branch information
rebornix committed Dec 7, 2016
1 parent c3f1fc8 commit 4f37dc1
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 63 deletions.
165 changes: 103 additions & 62 deletions src/vs/editor/contrib/linesOperations/common/linesOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { SortLinesCommand } from 'vs/editor/contrib/linesOperations/common/sortL
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { TrimTrailingWhitespaceCommand } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { EditorContextKeys, Handler, ICommand, ICommonCodeEditor, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { ReplaceCommand, ReplaceCommandThatPreservesSelection, ReplaceCommandWithOffsetCursorState } from 'vs/editor/common/commands/replaceCommand';
import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { editorAction, ServicesAccessor, IActionOptions, EditorAction, HandlerEditorAction } from 'vs/editor/common/editorCommonExtensions';
Expand Down Expand Up @@ -420,86 +420,127 @@ export class JoinLinesAction extends EditorAction {
}

public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
let selection = editor.getSelection();
let selections = editor.getSelections();
let primarySelection = editor.getSelection();

selections.sort(Range.compareRangesUsingStarts);
let reducedSelections: Selection[] = [];

let lastSelection = selections.reduce((previousValue, currentValue) => {
if (previousValue.isEmpty()) {
if (currentValue.startLineNumber > previousValue.endLineNumber + 1) {
reducedSelections.push(previousValue);
return currentValue;
} else {
return new Selection(previousValue.startLineNumber, previousValue.startColumn, currentValue.endLineNumber, currentValue.endColumn);
}
} else {
if (currentValue.startLineNumber > previousValue.endLineNumber) {
reducedSelections.push(previousValue);
return currentValue;
} else {
return new Selection(previousValue.startLineNumber, previousValue.startColumn, currentValue.endLineNumber, currentValue.endColumn);
}
}
});

reducedSelections.push(lastSelection);

let model = editor.getModel();
let startLineNumber = selection.startLineNumber;
let startColumn = 1;
let endLineNumber: number,
endColumn: number,
columnDeltaOffset: number;

let selectionEndPositionOffset = model.getLineContent(selection.endLineNumber).length - selection.endColumn;

if (selection.isEmpty() || selection.startLineNumber === selection.endLineNumber) {
let position = selection.getStartPosition();
if (position.lineNumber < model.getLineCount()) {
endLineNumber = startLineNumber + 1;
endColumn = model.getLineMaxColumn(endLineNumber);
let edits = [];
let resultSelections = [];
let resultPrimarySelection = primarySelection;
let lineOffset = 0;

for (let i = 0, len = reducedSelections.length; i < len; i++) {
let selection = reducedSelections[i];
let startLineNumber = selection.startLineNumber;
let startColumn = 1;
let endLineNumber: number,
endColumn: number,
columnDeltaOffset: number;

let selectionEndPositionOffset = model.getLineContent(selection.endLineNumber).length - selection.endColumn;

if (selection.isEmpty() || selection.startLineNumber === selection.endLineNumber) {
let position = selection.getStartPosition();
if (position.lineNumber < model.getLineCount()) {
endLineNumber = startLineNumber + 1;
endColumn = model.getLineMaxColumn(endLineNumber);
} else {
endLineNumber = position.lineNumber;
endColumn = model.getLineMaxColumn(position.lineNumber);
}
} else {
endLineNumber = position.lineNumber;
endColumn = model.getLineMaxColumn(position.lineNumber);
endLineNumber = selection.endLineNumber;
endColumn = model.getLineMaxColumn(endLineNumber);
}
} else {
endLineNumber = selection.endLineNumber;
endColumn = model.getLineMaxColumn(endLineNumber);
}

let trimmedLinesContent = model.getLineContent(startLineNumber);
let trimmedLinesContent = model.getLineContent(startLineNumber);

for (let i = startLineNumber + 1; i <= endLineNumber; i++) {
let lineText = model.getLineContent(i);
let firstNonWhitespaceIdx = model.getLineFirstNonWhitespaceColumn(i);
for (let i = startLineNumber + 1; i <= endLineNumber; i++) {
let lineText = model.getLineContent(i);
let firstNonWhitespaceIdx = model.getLineFirstNonWhitespaceColumn(i);

if (firstNonWhitespaceIdx >= 1) {
let insertSpace = true;
if (trimmedLinesContent === '') {
insertSpace = false;
}
if (firstNonWhitespaceIdx >= 1) {
let insertSpace = true;
if (trimmedLinesContent === '') {
insertSpace = false;
}

if (insertSpace && (trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' ||
trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t')) {
insertSpace = false;
trimmedLinesContent = trimmedLinesContent.replace(/[\s\uFEFF\xA0]+$/g, ' ');
}
if (insertSpace && (trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === ' ' ||
trimmedLinesContent.charAt(trimmedLinesContent.length - 1) === '\t')) {
insertSpace = false;
trimmedLinesContent = trimmedLinesContent.replace(/[\s\uFEFF\xA0]+$/g, ' ');
}

let lineTextWithoutIndent = lineText.substr(firstNonWhitespaceIdx - 1);
let lineTextWithoutIndent = lineText.substr(firstNonWhitespaceIdx - 1);

trimmedLinesContent += (insertSpace ? ' ' : '') + lineTextWithoutIndent;
trimmedLinesContent += (insertSpace ? ' ' : '') + lineTextWithoutIndent;

if (insertSpace) {
columnDeltaOffset = lineTextWithoutIndent.length + 1;
if (insertSpace) {
columnDeltaOffset = lineTextWithoutIndent.length + 1;
} else {
columnDeltaOffset = lineTextWithoutIndent.length;
}
} else {
columnDeltaOffset = lineTextWithoutIndent.length;
columnDeltaOffset = 0;
}
} else {
columnDeltaOffset = 0;
}
}

let deleteSelection = new Range(
startLineNumber,
startColumn,
endLineNumber,
endColumn
);
let deleteSelection = new Range(startLineNumber, startColumn, endLineNumber, endColumn);

if (!deleteSelection.isEmpty()) {
if (selection.isEmpty()) {
editor.executeCommand(this.id,
new ReplaceCommandWithOffsetCursorState(deleteSelection, trimmedLinesContent, 0, -columnDeltaOffset)
);
} else {
if (selection.startLineNumber === selection.endLineNumber) {
editor.executeCommand(this.id,
new ReplaceCommandThatPreservesSelection(deleteSelection, trimmedLinesContent, selection)
);
if (!deleteSelection.isEmpty()) {
let resultSelection: Selection;

if (selection.isEmpty()) {
edits.push(EditOperation.replace(deleteSelection, trimmedLinesContent));
resultSelection = new Selection(deleteSelection.startLineNumber - lineOffset, trimmedLinesContent.length - columnDeltaOffset + 1, startLineNumber - lineOffset, trimmedLinesContent.length - columnDeltaOffset + 1);
} else {
if (selection.startLineNumber === selection.endLineNumber) {
edits.push(EditOperation.replace(deleteSelection, trimmedLinesContent));
resultSelection = new Selection(selection.startLineNumber - lineOffset, selection.startColumn,
selection.endLineNumber - lineOffset, selection.endColumn);
} else {
edits.push(EditOperation.replace(deleteSelection, trimmedLinesContent));
resultSelection = new Selection(selection.startLineNumber - lineOffset, selection.startColumn,
selection.startLineNumber - lineOffset, trimmedLinesContent.length - selectionEndPositionOffset);
}
}

if (Range.intersectRanges(deleteSelection, primarySelection) !== null) {
resultPrimarySelection = resultSelection;
} else {
editor.executeCommand(this.id, new ReplaceCommand(deleteSelection, trimmedLinesContent));
editor.setSelection(new Selection(selection.startLineNumber, selection.startColumn,
selection.startLineNumber, trimmedLinesContent.length - selectionEndPositionOffset));
resultSelections.push(resultSelection);
}
}

lineOffset += deleteSelection.endLineNumber - deleteSelection.startLineNumber;
}

editor.executeEdits(this.id, edits);
resultSelections.unshift(resultPrimarySelection);
editor.setSelections(resultSelections);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ suite('Editor Contrib - Line Operations', () => {
});
});

test('Join lines', function () {
test('join lines', function () {
withMockCodeEditor(
[
'hello',
Expand Down Expand Up @@ -113,6 +113,50 @@ suite('Editor Contrib - Line Operations', () => {
});
});

test('join lines in multi cursor mode', function () {
withMockCodeEditor(
[
'hello',
'world',
'hello ',
'world',
'hello ',
' world',
'hello ',
' world',
'',
'',
'hello world'
], {}, (editor, cursor) => {
let model = editor.getModel();
let joinLinesAction = new JoinLinesAction();

editor.setSelections([
/** primary cursor */
new Selection(5, 2, 5, 2),
new Selection(1, 2, 1, 2),
new Selection(3, 2, 4, 2),
new Selection(5, 4, 6, 3),
new Selection(7, 5, 8, 4),
new Selection(10, 1, 10, 1)
]);

joinLinesAction.run(null, editor);
assert.equal(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world', '001');
assert.deepEqual(editor.getSelections().toString(), [
/** primary cursor */
new Selection(3, 2, 3, 8),
new Selection(1, 6, 1, 6),
new Selection(2, 2, 2, 8),
new Selection(4, 5, 4, 9),
new Selection(6, 1, 6, 1)
].toString(), '002');

/** primary cursor */
assert.deepEqual(editor.getSelection().toString(), new Selection(3, 2, 3, 8).toString(), '003');
});
});

test('transpose', function () {
withMockCodeEditor(
[
Expand Down

0 comments on commit 4f37dc1

Please sign in to comment.