Skip to content

Commit 84e23b5

Browse files
authored
Merge pull request microsoft#17152 from rebornix/MultiCursorState
Compute cursor/s state after edits were applied
2 parents a1a4727 + 9dbe45d commit 84e23b5

5 files changed

Lines changed: 291 additions & 207 deletions

File tree

src/vs/editor/common/commonCodeEditor.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
600600
return true;
601601
}
602602

603-
public executeEdits(source: string, edits: editorCommon.IIdentifiedSingleEditOperation[]): boolean {
603+
public executeEdits(source: string, edits: editorCommon.IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean {
604604
if (!this.cursor) {
605605
// no view, no cursor
606606
return false;
@@ -611,9 +611,13 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
611611
}
612612

613613
this.model.pushEditOperations(this.cursor.getSelections(), edits, () => {
614-
return this.cursor.getSelections();
614+
return endCursorState ? endCursorState : this.cursor.getSelections();
615615
});
616616

617+
if (endCursorState) {
618+
this.cursor.setSelections(source, endCursorState);
619+
}
620+
617621
return true;
618622
}
619623

src/vs/editor/common/editorCommon.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3939,11 +3939,12 @@ export interface ICommonCodeEditor extends IEditor {
39393939
pushUndoStop(): boolean;
39403940

39413941
/**
3942-
* Execute a command on the editor.
3942+
* Execute edits on the editor.
39433943
* @param source The source of the call.
3944-
* @param command The command to execute
3944+
* @param edits The edits to execute.
3945+
* @param endCursoState Cursor state after the edits were applied.
39453946
*/
3946-
executeEdits(source: string, edits: IIdentifiedSingleEditOperation[]): boolean;
3947+
executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursoState?: Selection[]): boolean;
39473948

39483949
/**
39493950
* Execute multiple (concommitent) commands on the editor.

src/vs/editor/contrib/linesOperations/common/linesOperations.ts

Lines changed: 98 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,45 @@ class InsertLineAfterAction extends HandlerEditorAction {
351351
}
352352
}
353353

354+
export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction {
355+
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
356+
const primaryCursor = editor.getSelection();
357+
let rangesToDelete = this._getRangesToDelete(editor);
358+
// merge overlapping selections
359+
let effectiveRanges: Range[] = [];
360+
361+
for (let i = 0, count = rangesToDelete.length - 1; i < count; i++) {
362+
let range = rangesToDelete[i];
363+
let nextRange = rangesToDelete[i + 1];
364+
365+
if (Range.intersectRanges(range, nextRange) === null) {
366+
effectiveRanges.push(range);
367+
} else {
368+
rangesToDelete[i + 1] = Range.plusRange(range, nextRange);
369+
}
370+
}
371+
372+
effectiveRanges.push(rangesToDelete[rangesToDelete.length - 1]);
373+
374+
let endCursorState = this._getEndCursorState(primaryCursor, effectiveRanges);
375+
let edits: IIdentifiedSingleEditOperation[] = effectiveRanges.map(range => {
376+
endCursorState.push(new Selection(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn));
377+
return EditOperation.replace(range, '');
378+
});
379+
380+
editor.executeEdits(this.id, edits, endCursorState);
381+
}
382+
383+
/**
384+
* Compute the cursor state after the edit operations were applied.
385+
*/
386+
protected abstract _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[];
387+
388+
protected abstract _getRangesToDelete(editor: ICommonCodeEditor): Range[];
389+
}
390+
354391
@editorAction
355-
export class DeleteAllLeftAction extends EditorAction {
392+
export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction {
356393
constructor() {
357394
super({
358395
id: 'deleteAllLeft',
@@ -367,44 +404,46 @@ export class DeleteAllLeftAction extends EditorAction {
367404
});
368405
}
369406

370-
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
371-
let selections: Range[] = editor.getSelections();
407+
_getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] {
408+
let endPrimaryCursor: Range;
409+
let endCursorState = [];
372410

373-
selections.sort(Range.compareRangesUsingStarts);
374-
selections = selections.map(selection => {
375-
if (selection.isEmpty()) {
376-
return new Selection(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn);
411+
for (let i = 0, len = rangesToDelete.length; i < len; i++) {
412+
let range = rangesToDelete[i];
413+
let endCursor = new Selection(rangesToDelete[i].startLineNumber, rangesToDelete[i].startColumn, rangesToDelete[i].startLineNumber, rangesToDelete[i].startColumn);
414+
415+
if (range.intersectRanges(primaryCursor)) {
416+
endPrimaryCursor = endCursor;
377417
} else {
378-
return selection;
418+
endCursorState.push(endCursor);
379419
}
380-
});
420+
}
381421

382-
// merge overlapping selections
383-
let effectiveRanges: Range[] = [];
422+
if (endPrimaryCursor) {
423+
endCursorState.unshift(endPrimaryCursor);
424+
}
384425

385-
for (let i = 0, count = selections.length - 1; i < count; i++) {
386-
let range = selections[i];
387-
let nextRange = selections[i + 1];
426+
return endCursorState;
427+
}
388428

389-
if (Range.intersectRanges(range, nextRange) === null) {
390-
effectiveRanges.push(range);
429+
_getRangesToDelete(editor: ICommonCodeEditor): Range[] {
430+
let rangesToDelete: Range[] = editor.getSelections();
431+
432+
rangesToDelete.sort(Range.compareRangesUsingStarts);
433+
rangesToDelete = rangesToDelete.map(selection => {
434+
if (selection.isEmpty()) {
435+
return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn);
391436
} else {
392-
selections[i + 1] = Range.plusRange(range, nextRange);
437+
return selection;
393438
}
394-
}
395-
396-
effectiveRanges.push(selections[selections.length - 1]);
397-
398-
let edits: IIdentifiedSingleEditOperation[] = effectiveRanges.map(range => {
399-
return EditOperation.replace(range, '');
400439
});
401440

402-
editor.executeEdits(this.id, edits);
441+
return rangesToDelete;
403442
}
404443
}
405444

406445
@editorAction
407-
export class DeleteAllRightAction extends EditorAction {
446+
export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction {
408447
constructor() {
409448
super({
410449
id: 'deleteAllRight',
@@ -419,7 +458,28 @@ export class DeleteAllRightAction extends EditorAction {
419458
});
420459
}
421460

422-
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
461+
_getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] {
462+
let endPrimaryCursor: Range;
463+
let endCursorState = [];
464+
for (let i = 0, len = rangesToDelete.length, offset = 0; i < len; i++) {
465+
let range = rangesToDelete[i];
466+
let endCursor = new Selection(range.startLineNumber - offset, range.startColumn, range.startLineNumber - offset, range.startColumn);
467+
468+
if (range.intersectRanges(primaryCursor)) {
469+
endPrimaryCursor = endCursor;
470+
} else {
471+
endCursorState.push(endCursor);
472+
}
473+
}
474+
475+
if (endPrimaryCursor) {
476+
endCursorState.unshift(endPrimaryCursor);
477+
}
478+
479+
return endCursorState;
480+
}
481+
482+
_getRangesToDelete(editor: ICommonCodeEditor): Range[] {
423483
let model = editor.getModel();
424484

425485
let rangesToDelete: Range[] = editor.getSelections().map((sel) => {
@@ -436,29 +496,7 @@ export class DeleteAllRightAction extends EditorAction {
436496
});
437497

438498
rangesToDelete.sort(Range.compareRangesUsingStarts);
439-
440-
// merge overlapping selections
441-
let effectiveRanges: Range[] = [];
442-
443-
for (let i = 0, count = rangesToDelete.length - 1; i < count; i++) {
444-
let range = rangesToDelete[i];
445-
let nextRange = rangesToDelete[i + 1];
446-
447-
if (Range.intersectRanges(range, nextRange) === null) {
448-
effectiveRanges.push(range);
449-
} else {
450-
rangesToDelete[i + 1] = Range.plusRange(range, nextRange);
451-
}
452-
}
453-
454-
effectiveRanges.push(rangesToDelete[rangesToDelete.length - 1]);
455-
456-
let edits: IIdentifiedSingleEditOperation[] = effectiveRanges.map(range => {
457-
return EditOperation.replace(range, '');
458-
});
459-
460-
editor.executeEdits(this.id, edits);
461-
editor.pushUndoStop();
499+
return rangesToDelete;
462500
}
463501
}
464502

@@ -480,16 +518,16 @@ export class JoinLinesAction extends EditorAction {
480518

481519
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
482520
let selections = editor.getSelections();
483-
let primarySelection = editor.getSelection();
521+
let primaryCursor = editor.getSelection();
484522

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

488526
let lastSelection = selections.reduce((previousValue, currentValue) => {
489527
if (previousValue.isEmpty()) {
490528
if (previousValue.endLineNumber === currentValue.startLineNumber) {
491-
if (primarySelection.equalsSelection(previousValue)) {
492-
primarySelection = currentValue;
529+
if (primaryCursor.equalsSelection(previousValue)) {
530+
primaryCursor = currentValue;
493531
}
494532
return currentValue;
495533
}
@@ -514,8 +552,8 @@ export class JoinLinesAction extends EditorAction {
514552

515553
let model = editor.getModel();
516554
let edits = [];
517-
let resultSelections = [];
518-
let resultPrimarySelection = primarySelection;
555+
let endCursorState = [];
556+
let endPrimaryCursor = primaryCursor;
519557
let lineOffset = 0;
520558

521559
for (let i = 0, len = reducedSelections.length; i < len; i++) {
@@ -594,19 +632,19 @@ export class JoinLinesAction extends EditorAction {
594632
}
595633
}
596634

597-
if (Range.intersectRanges(deleteSelection, primarySelection) !== null) {
598-
resultPrimarySelection = resultSelection;
635+
if (Range.intersectRanges(deleteSelection, primaryCursor) !== null) {
636+
endPrimaryCursor = resultSelection;
599637
} else {
600-
resultSelections.push(resultSelection);
638+
endCursorState.push(resultSelection);
601639
}
602640
}
603641

604642
lineOffset += deleteSelection.endLineNumber - deleteSelection.startLineNumber;
605643
}
606644

607-
editor.executeEdits(this.id, edits);
608-
resultSelections.unshift(resultPrimarySelection);
609-
editor.setSelections(resultSelections);
645+
endCursorState.unshift(endPrimaryCursor);
646+
editor.executeEdits(this.id, edits, endCursorState);
647+
610648
}
611649
}
612650

0 commit comments

Comments
 (0)