mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
fix #16587. Enrich Join lines with multi cursor support
This commit is contained in:
@@ -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';
|
||||
@@ -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 edits = [];
|
||||
let resultSelections = [];
|
||||
let resultPrimarySelection = primarySelection;
|
||||
let lineOffset = 0;
|
||||
|
||||
let selectionEndPositionOffset = model.getLineContent(selection.endLineNumber).length - selection.endColumn;
|
||||
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;
|
||||
|
||||
if (selection.isEmpty() || selection.startLineNumber === selection.endLineNumber) {
|
||||
let position = selection.getStartPosition();
|
||||
if (position.lineNumber < model.getLineCount()) {
|
||||
endLineNumber = startLineNumber + 1;
|
||||
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 = selection.endLineNumber;
|
||||
endColumn = model.getLineMaxColumn(endLineNumber);
|
||||
} else {
|
||||
endLineNumber = position.lineNumber;
|
||||
endColumn = model.getLineMaxColumn(position.lineNumber);
|
||||
}
|
||||
} 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 {
|
||||
editor.executeCommand(this.id, new ReplaceCommand(deleteSelection, trimmedLinesContent));
|
||||
editor.setSelection(new Selection(selection.startLineNumber, selection.startColumn,
|
||||
selection.startLineNumber, trimmedLinesContent.length - selectionEndPositionOffset));
|
||||
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 {
|
||||
resultSelections.push(resultSelection);
|
||||
}
|
||||
}
|
||||
|
||||
lineOffset += deleteSelection.endLineNumber - deleteSelection.startLineNumber;
|
||||
}
|
||||
|
||||
editor.executeEdits(this.id, edits);
|
||||
resultSelections.unshift(resultPrimarySelection);
|
||||
editor.setSelections(resultSelections);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ suite('Editor Contrib - Line Operations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Join lines', function () {
|
||||
test('join lines', function () {
|
||||
withMockCodeEditor(
|
||||
[
|
||||
'hello',
|
||||
@@ -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(
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user