Files
azuredatastudio/src/vs/editor/common/controller/cursorCollection.ts
Charles Gagnon 2bc6a0cd01 VS Code merge to df8fe74bd55313de0dd2303bc47a4aab0ca56b0e (#17979)
* Merge from vscode 504f934659740e9d41501cad9f162b54d7745ad9

* delete unused folders

* distro

* Bump build node version

* update chokidar

* FIx hygiene errors

* distro

* Fix extension lint issues

* Remove strict-vscode

* Add copyright header exemptions

* Bump vscode-extension-telemetry to fix webpacking issue with zone.js

* distro

* Fix failing tests (revert marked.js back to current one until we decide to update)

* Skip searchmodel test

* Fix mac build

* temp debug script loading

* Try disabling coverage

* log error too

* Revert "log error too"

This reverts commit af0183e5d4ab458fdf44b88fbfab9908d090526f.

* Revert "temp debug script loading"

This reverts commit 3d687d541c76db2c5b55626c78ae448d3c25089c.

* Add comments explaining coverage disabling

* Fix ansi_up loading issue

* Merge latest from ads

* Use newer option

* Fix compile

* add debug logging warn

* Always log stack

* log more

* undo debug

* Update to use correct base path (+cleanup)

* distro

* fix compile errors

* Remove strict-vscode

* Fix sql editors not showing

* Show db dropdown input & fix styling

* Fix more info in gallery

* Fix gallery asset requests

* Delete unused workflow

* Fix tapable resolutions for smoke test compile error

* Fix smoke compile

* Disable crash reporting

* Disable interactive

Co-authored-by: ADS Merger <karlb@microsoft.com>
2022-01-06 09:06:56 -08:00

294 lines
10 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CursorContext, CursorState, PartialCursorState } from 'vs/editor/common/controller/cursorCommon';
import { Cursor } from 'vs/editor/common/controller/oneCursor';
import { Position } from 'vs/editor/common/core/position';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
export class CursorCollection {
private context: CursorContext;
private primaryCursor: Cursor;
private secondaryCursors: Cursor[];
// An index which identifies the last cursor that was added / moved (think Ctrl+drag)
private lastAddedCursorIndex: number;
constructor(context: CursorContext) {
this.context = context;
this.primaryCursor = new Cursor(context);
this.secondaryCursors = [];
this.lastAddedCursorIndex = 0;
}
public dispose(): void {
this.primaryCursor.dispose(this.context);
this.killSecondaryCursors();
}
public startTrackingSelections(): void {
this.primaryCursor.startTrackingSelection(this.context);
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
this.secondaryCursors[i].startTrackingSelection(this.context);
}
}
public stopTrackingSelections(): void {
this.primaryCursor.stopTrackingSelection(this.context);
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
this.secondaryCursors[i].stopTrackingSelection(this.context);
}
}
public updateContext(context: CursorContext): void {
this.context = context;
}
public ensureValidState(): void {
this.primaryCursor.ensureValidState(this.context);
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
this.secondaryCursors[i].ensureValidState(this.context);
}
}
public readSelectionFromMarkers(): Selection[] {
let result: Selection[] = [];
result[0] = this.primaryCursor.readSelectionFromMarkers(this.context);
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
result[i + 1] = this.secondaryCursors[i].readSelectionFromMarkers(this.context);
}
return result;
}
public getAll(): CursorState[] {
let result: CursorState[] = [];
result[0] = this.primaryCursor.asCursorState();
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
result[i + 1] = this.secondaryCursors[i].asCursorState();
}
return result;
}
public getViewPositions(): Position[] {
let result: Position[] = [];
result[0] = this.primaryCursor.viewState.position;
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
result[i + 1] = this.secondaryCursors[i].viewState.position;
}
return result;
}
public getTopMostViewPosition(): Position {
let result = this.primaryCursor.viewState.position;
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
const viewPosition = this.secondaryCursors[i].viewState.position;
if (viewPosition.isBefore(result)) {
result = viewPosition;
}
}
return result;
}
public getBottomMostViewPosition(): Position {
let result = this.primaryCursor.viewState.position;
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
const viewPosition = this.secondaryCursors[i].viewState.position;
if (result.isBeforeOrEqual(viewPosition)) {
result = viewPosition;
}
}
return result;
}
public getSelections(): Selection[] {
let result: Selection[] = [];
result[0] = this.primaryCursor.modelState.selection;
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
result[i + 1] = this.secondaryCursors[i].modelState.selection;
}
return result;
}
public getViewSelections(): Selection[] {
let result: Selection[] = [];
result[0] = this.primaryCursor.viewState.selection;
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
result[i + 1] = this.secondaryCursors[i].viewState.selection;
}
return result;
}
public setSelections(selections: ISelection[]): void {
this.setStates(CursorState.fromModelSelections(selections));
}
public getPrimaryCursor(): CursorState {
return this.primaryCursor.asCursorState();
}
public setStates(states: PartialCursorState[] | null): void {
if (states === null) {
return;
}
this.primaryCursor.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`.
*/
private _setSecondaryStates(secondaryStates: PartialCursorState[]): void {
const secondaryCursorsLength = this.secondaryCursors.length;
const secondaryStatesLength = secondaryStates.length;
if (secondaryCursorsLength < secondaryStatesLength) {
let createCnt = secondaryStatesLength - secondaryCursorsLength;
for (let i = 0; i < createCnt; i++) {
this._addSecondaryCursor();
}
} else if (secondaryCursorsLength > secondaryStatesLength) {
let removeCnt = secondaryCursorsLength - secondaryStatesLength;
for (let i = 0; i < removeCnt; i++) {
this._removeSecondaryCursor(this.secondaryCursors.length - 1);
}
}
for (let i = 0; i < secondaryStatesLength; i++) {
this.secondaryCursors[i].setState(this.context, secondaryStates[i].modelState, secondaryStates[i].viewState);
}
}
public killSecondaryCursors(): void {
this._setSecondaryStates([]);
}
private _addSecondaryCursor(): void {
this.secondaryCursors.push(new Cursor(this.context));
this.lastAddedCursorIndex = this.secondaryCursors.length;
}
public getLastAddedCursorIndex(): number {
if (this.secondaryCursors.length === 0 || this.lastAddedCursorIndex === 0) {
return 0;
}
return this.lastAddedCursorIndex;
}
private _removeSecondaryCursor(removeIndex: number): void {
if (this.lastAddedCursorIndex >= removeIndex + 1) {
this.lastAddedCursorIndex--;
}
this.secondaryCursors[removeIndex].dispose(this.context);
this.secondaryCursors.splice(removeIndex, 1);
}
private _getAll(): Cursor[] {
let result: Cursor[] = [];
result[0] = this.primaryCursor;
for (let i = 0, len = this.secondaryCursors.length; i < len; i++) {
result[i + 1] = this.secondaryCursors[i];
}
return result;
}
public normalize(): void {
if (this.secondaryCursors.length === 0) {
return;
}
let cursors = this._getAll();
interface SortedCursor {
index: number;
selection: Selection;
}
let sortedCursors: SortedCursor[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
sortedCursors.push({
index: i,
selection: cursors[i].modelState.selection,
});
}
sortedCursors.sort((a, b) => {
if (a.selection.startLineNumber === b.selection.startLineNumber) {
return a.selection.startColumn - b.selection.startColumn;
}
return a.selection.startLineNumber - b.selection.startLineNumber;
});
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: boolean;
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: boolean;
if (looserIndex === this.lastAddedCursorIndex) {
resultingSelectionIsLTR = looserSelectionIsLTR;
this.lastAddedCursorIndex = winnerIndex;
} else {
// Winner takes it all
resultingSelectionIsLTR = winnerSelectionIsLTR;
}
let resultingSelection: Selection;
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--;
}
}
}
}