| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { compareBy, findLastMaxBy, findMinBy } from '../../../base/common/arrays.js';
- import { CursorState } from '../cursorCommon.js';
- import { Cursor } from './oneCursor.js';
- import { Position } from '../core/position.js';
- import { Range } from '../core/range.js';
- import { Selection } from '../core/selection.js';
- export class CursorCollection {
- constructor(context) {
- this.context = context;
- this.cursors = [new Cursor(context)];
- this.lastAddedCursorIndex = 0;
- }
- dispose() {
- for (const cursor of this.cursors) {
- cursor.dispose(this.context);
- }
- }
- startTrackingSelections() {
- for (const cursor of this.cursors) {
- cursor.startTrackingSelection(this.context);
- }
- }
- stopTrackingSelections() {
- for (const cursor of this.cursors) {
- cursor.stopTrackingSelection(this.context);
- }
- }
- updateContext(context) {
- this.context = context;
- }
- ensureValidState() {
- for (const cursor of this.cursors) {
- cursor.ensureValidState(this.context);
- }
- }
- readSelectionFromMarkers() {
- return this.cursors.map(c => c.readSelectionFromMarkers(this.context));
- }
- getAll() {
- return this.cursors.map(c => c.asCursorState());
- }
- getViewPositions() {
- return this.cursors.map(c => c.viewState.position);
- }
- getTopMostViewPosition() {
- return findMinBy(this.cursors, compareBy(c => c.viewState.position, Position.compare)).viewState.position;
- }
- getBottomMostViewPosition() {
- return findLastMaxBy(this.cursors, compareBy(c => c.viewState.position, Position.compare)).viewState.position;
- }
- getSelections() {
- return this.cursors.map(c => c.modelState.selection);
- }
- getViewSelections() {
- return this.cursors.map(c => c.viewState.selection);
- }
- setSelections(selections) {
- this.setStates(CursorState.fromModelSelections(selections));
- }
- getPrimaryCursor() {
- return this.cursors[0].asCursorState();
- }
- setStates(states) {
- if (states === null) {
- return;
- }
- this.cursors[0].setState(this.context, states[0].modelState, states[0].viewState);
- this._setSecondaryStates(states.slice(1));
- }
- /**
- * Creates or disposes secondary cursors as necessary to match the number of `secondarySelections`.
- */
- _setSecondaryStates(secondaryStates) {
- const secondaryCursorsLength = this.cursors.length - 1;
- const secondaryStatesLength = secondaryStates.length;
- if (secondaryCursorsLength < secondaryStatesLength) {
- const createCnt = secondaryStatesLength - secondaryCursorsLength;
- for (let i = 0; i < createCnt; i++) {
- this._addSecondaryCursor();
- }
- }
- else if (secondaryCursorsLength > secondaryStatesLength) {
- const removeCnt = secondaryCursorsLength - secondaryStatesLength;
- for (let i = 0; i < removeCnt; i++) {
- this._removeSecondaryCursor(this.cursors.length - 2);
- }
- }
- for (let i = 0; i < secondaryStatesLength; i++) {
- this.cursors[i + 1].setState(this.context, secondaryStates[i].modelState, secondaryStates[i].viewState);
- }
- }
- killSecondaryCursors() {
- this._setSecondaryStates([]);
- }
- _addSecondaryCursor() {
- this.cursors.push(new Cursor(this.context));
- this.lastAddedCursorIndex = this.cursors.length - 1;
- }
- getLastAddedCursorIndex() {
- if (this.cursors.length === 1 || this.lastAddedCursorIndex === 0) {
- return 0;
- }
- return this.lastAddedCursorIndex;
- }
- _removeSecondaryCursor(removeIndex) {
- if (this.lastAddedCursorIndex >= removeIndex + 1) {
- this.lastAddedCursorIndex--;
- }
- this.cursors[removeIndex + 1].dispose(this.context);
- this.cursors.splice(removeIndex + 1, 1);
- }
- normalize() {
- if (this.cursors.length === 1) {
- return;
- }
- const cursors = this.cursors.slice(0);
- const sortedCursors = [];
- for (let i = 0, len = cursors.length; i < len; i++) {
- sortedCursors.push({
- index: i,
- selection: cursors[i].modelState.selection,
- });
- }
- sortedCursors.sort(compareBy(s => s.selection, Range.compareRangesUsingStarts));
- for (let sortedCursorIndex = 0; sortedCursorIndex < sortedCursors.length - 1; sortedCursorIndex++) {
- const current = sortedCursors[sortedCursorIndex];
- const next = sortedCursors[sortedCursorIndex + 1];
- const currentSelection = current.selection;
- const nextSelection = next.selection;
- if (!this.context.cursorConfig.multiCursorMergeOverlapping) {
- continue;
- }
- let shouldMergeCursors;
- if (nextSelection.isEmpty() || currentSelection.isEmpty()) {
- // Merge touching cursors if one of them is collapsed
- shouldMergeCursors = nextSelection.getStartPosition().isBeforeOrEqual(currentSelection.getEndPosition());
- }
- else {
- // Merge only overlapping cursors (i.e. allow touching ranges)
- shouldMergeCursors = nextSelection.getStartPosition().isBefore(currentSelection.getEndPosition());
- }
- if (shouldMergeCursors) {
- const winnerSortedCursorIndex = current.index < next.index ? sortedCursorIndex : sortedCursorIndex + 1;
- const looserSortedCursorIndex = current.index < next.index ? sortedCursorIndex + 1 : sortedCursorIndex;
- const looserIndex = sortedCursors[looserSortedCursorIndex].index;
- const winnerIndex = sortedCursors[winnerSortedCursorIndex].index;
- const looserSelection = sortedCursors[looserSortedCursorIndex].selection;
- const winnerSelection = sortedCursors[winnerSortedCursorIndex].selection;
- if (!looserSelection.equalsSelection(winnerSelection)) {
- const resultingRange = looserSelection.plusRange(winnerSelection);
- const looserSelectionIsLTR = (looserSelection.selectionStartLineNumber === looserSelection.startLineNumber && looserSelection.selectionStartColumn === looserSelection.startColumn);
- const winnerSelectionIsLTR = (winnerSelection.selectionStartLineNumber === winnerSelection.startLineNumber && winnerSelection.selectionStartColumn === winnerSelection.startColumn);
- // Give more importance to the last added cursor (think Ctrl-dragging + hitting another cursor)
- let resultingSelectionIsLTR;
- if (looserIndex === this.lastAddedCursorIndex) {
- resultingSelectionIsLTR = looserSelectionIsLTR;
- this.lastAddedCursorIndex = winnerIndex;
- }
- else {
- // Winner takes it all
- resultingSelectionIsLTR = winnerSelectionIsLTR;
- }
- let resultingSelection;
- if (resultingSelectionIsLTR) {
- resultingSelection = new Selection(resultingRange.startLineNumber, resultingRange.startColumn, resultingRange.endLineNumber, resultingRange.endColumn);
- }
- else {
- resultingSelection = new Selection(resultingRange.endLineNumber, resultingRange.endColumn, resultingRange.startLineNumber, resultingRange.startColumn);
- }
- sortedCursors[winnerSortedCursorIndex].selection = resultingSelection;
- const resultingState = CursorState.fromModelSelection(resultingSelection);
- cursors[winnerIndex].setState(this.context, resultingState.modelState, resultingState.viewState);
- }
- for (const sortedCursor of sortedCursors) {
- if (sortedCursor.index > looserIndex) {
- sortedCursor.index--;
- }
- }
- cursors.splice(looserIndex, 1);
- sortedCursors.splice(looserSortedCursorIndex, 1);
- this._removeSecondaryCursor(looserIndex - 1);
- sortedCursorIndex--;
- }
- }
- }
- }
|