Path: blob/main/src/vs/editor/common/cursor/oneCursor.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { CursorState, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from '../cursorCommon.js';6import { CursorContext } from './cursorContext.js';7import { Position } from '../core/position.js';8import { Range } from '../core/range.js';9import { Selection } from '../core/selection.js';10import { PositionAffinity, TrackedRangeStickiness } from '../model.js';1112/**13* Represents a single cursor.14*/15export class Cursor {1617public modelState!: SingleCursorState;18public viewState!: SingleCursorState;1920private _selTrackedRange: string | null;21private _trackSelection: boolean;2223constructor(context: CursorContext) {24this._selTrackedRange = null;25this._trackSelection = true;2627this._setState(28context,29new SingleCursorState(new Range(1, 1, 1, 1), SelectionStartKind.Simple, 0, new Position(1, 1), 0),30new SingleCursorState(new Range(1, 1, 1, 1), SelectionStartKind.Simple, 0, new Position(1, 1), 0)31);32}3334public dispose(context: CursorContext): void {35this._removeTrackedRange(context);36}3738public startTrackingSelection(context: CursorContext): void {39this._trackSelection = true;40this._updateTrackedRange(context);41}4243public stopTrackingSelection(context: CursorContext): void {44this._trackSelection = false;45this._removeTrackedRange(context);46}4748private _updateTrackedRange(context: CursorContext): void {49if (!this._trackSelection) {50// don't track the selection51return;52}53this._selTrackedRange = context.model._setTrackedRange(this._selTrackedRange, this.modelState.selection, TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges);54}5556private _removeTrackedRange(context: CursorContext): void {57this._selTrackedRange = context.model._setTrackedRange(this._selTrackedRange, null, TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges);58}5960public asCursorState(): CursorState {61return new CursorState(this.modelState, this.viewState);62}6364public readSelectionFromMarkers(context: CursorContext): Selection {65const range = context.model._getTrackedRange(this._selTrackedRange!)!;6667if (this.modelState.selection.isEmpty() && !range.isEmpty()) {68// Avoid selecting text when recovering from markers69return Selection.fromRange(range.collapseToEnd(), this.modelState.selection.getDirection());70}7172return Selection.fromRange(range, this.modelState.selection.getDirection());73}7475public ensureValidState(context: CursorContext): void {76this._setState(context, this.modelState, this.viewState);77}7879public setState(context: CursorContext, modelState: SingleCursorState | null, viewState: SingleCursorState | null): void {80this._setState(context, modelState, viewState);81}8283private static _validatePositionWithCache(viewModel: ICursorSimpleModel, position: Position, cacheInput: Position, cacheOutput: Position): Position {84if (position.equals(cacheInput)) {85return cacheOutput;86}87return viewModel.normalizePosition(position, PositionAffinity.None);88}8990private static _validateViewState(viewModel: ICursorSimpleModel, viewState: SingleCursorState): SingleCursorState {91const position = viewState.position;92const sStartPosition = viewState.selectionStart.getStartPosition();93const sEndPosition = viewState.selectionStart.getEndPosition();9495const validPosition = viewModel.normalizePosition(position, PositionAffinity.None);96const validSStartPosition = this._validatePositionWithCache(viewModel, sStartPosition, position, validPosition);97const validSEndPosition = this._validatePositionWithCache(viewModel, sEndPosition, sStartPosition, validSStartPosition);9899if (position.equals(validPosition) && sStartPosition.equals(validSStartPosition) && sEndPosition.equals(validSEndPosition)) {100// fast path: the state is valid101return viewState;102}103104return new SingleCursorState(105Range.fromPositions(validSStartPosition, validSEndPosition),106viewState.selectionStartKind,107viewState.selectionStartLeftoverVisibleColumns + sStartPosition.column - validSStartPosition.column,108validPosition,109viewState.leftoverVisibleColumns + position.column - validPosition.column,110);111}112113private _setState(context: CursorContext, modelState: SingleCursorState | null, viewState: SingleCursorState | null): void {114if (viewState) {115viewState = Cursor._validateViewState(context.viewModel, viewState);116}117118if (!modelState) {119if (!viewState) {120return;121}122// We only have the view state => compute the model state123const selectionStart = context.model.validateRange(124context.coordinatesConverter.convertViewRangeToModelRange(viewState.selectionStart)125);126127const position = context.model.validatePosition(128context.coordinatesConverter.convertViewPositionToModelPosition(viewState.position)129);130131modelState = new SingleCursorState(selectionStart, viewState.selectionStartKind, viewState.selectionStartLeftoverVisibleColumns, position, viewState.leftoverVisibleColumns);132} else {133// Validate new model state134const selectionStart = context.model.validateRange(modelState.selectionStart);135const selectionStartLeftoverVisibleColumns = modelState.selectionStart.equalsRange(selectionStart) ? modelState.selectionStartLeftoverVisibleColumns : 0;136137const position = context.model.validatePosition(138modelState.position139);140const leftoverVisibleColumns = modelState.position.equals(position) ? modelState.leftoverVisibleColumns : 0;141142modelState = new SingleCursorState(selectionStart, modelState.selectionStartKind, selectionStartLeftoverVisibleColumns, position, leftoverVisibleColumns);143}144145if (!viewState) {146// We only have the model state => compute the view state147const viewSelectionStart1 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn));148const viewSelectionStart2 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn));149const viewSelectionStart = new Range(viewSelectionStart1.lineNumber, viewSelectionStart1.column, viewSelectionStart2.lineNumber, viewSelectionStart2.column);150const viewPosition = context.coordinatesConverter.convertModelPositionToViewPosition(modelState.position);151viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartKind, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns);152} else {153// Validate new view state154const viewSelectionStart = context.coordinatesConverter.validateViewRange(viewState.selectionStart, modelState.selectionStart);155const viewPosition = context.coordinatesConverter.validateViewPosition(viewState.position, modelState.position);156viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartKind, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns);157}158159this.modelState = modelState;160this.viewState = viewState;161162this._updateTrackedRange(context);163}164}165166167