Merge from vscode 79a1f5a5ca0c6c53db617aa1fa5a2396d2caebe2

This commit is contained in:
ADS Merger
2020-05-31 19:47:51 +00:00
parent 84492049e8
commit 28be33cfea
913 changed files with 28242 additions and 15549 deletions

View File

@@ -283,6 +283,9 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
private _onDidChange = this._register(new Emitter<ConfigurationChangedEvent>());
public readonly onDidChange: Event<ConfigurationChangedEvent> = this._onDidChange.event;
private _onDidChangeFast = this._register(new Emitter<ConfigurationChangedEvent>());
public readonly onDidChangeFast: Event<ConfigurationChangedEvent> = this._onDidChangeFast.event;
public readonly isSimpleWidget: boolean;
private _computeOptionsMemory: ComputeOptionsMemory;
public options!: ComputedEditorOptions;
@@ -334,6 +337,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
}
this.options = newOptions;
this._onDidChangeFast.fire(changeEvent);
this._onDidChange.fire(changeEvent);
}
}

View File

@@ -93,6 +93,11 @@ export interface IEditorOptions {
* Defaults to true.
*/
renderFinalNewline?: boolean;
/**
* Remove unusual line terminators like LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS), NEXT LINE (NEL).
* Defaults to true.
*/
removeUnusualLineTerminators?: boolean;
/**
* Should the corresponding line be selected when clicking on the line number?
* Defaults to true.
@@ -2878,8 +2883,8 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
useShadows: true,
verticalHasArrows: false,
horizontalHasArrows: false,
horizontalScrollbarSize: 10,
horizontalSliderSize: 10,
horizontalScrollbarSize: 12,
horizontalSliderSize: 12,
verticalScrollbarSize: 14,
verticalSliderSize: 14,
handleMouseWheel: true,
@@ -3555,6 +3560,7 @@ export const enum EditorOption {
quickSuggestions,
quickSuggestionsDelay,
readOnly,
removeUnusualLineTerminators,
renameOnType,
renderControlCharacters,
renderIndentGuides,
@@ -3973,6 +3979,10 @@ export const EditorOptions = {
readOnly: register(new EditorBooleanOption(
EditorOption.readOnly, 'readOnly', false,
)),
removeUnusualLineTerminators: register(new EditorBooleanOption(
EditorOption.removeUnusualLineTerminators, 'removeUnusualLineTerminators', true,
{ description: nls.localize('removeUnusualLineTerminators', "Remove unusual line terminators that might cause problems.") }
)),
renameOnType: register(new EditorBooleanOption(
EditorOption.renameOnType, 'renameOnType', false,
{ description: nls.localize('renameOnType', "Controls whether the editor auto renames on type.") }

View File

@@ -4,10 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import * as strings from 'vs/base/common/strings';
import { CursorCollection } from 'vs/editor/common/controller/cursorCollection';
import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, ICursors, PartialCursorState, RevealTarget } from 'vs/editor/common/controller/cursorCommon';
import { CursorColumns, CursorConfiguration, CursorContext, CursorState, EditOperationResult, EditOperationType, IColumnSelectData, PartialCursorState, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon';
import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { TypeOperations, TypeWithAutoClosingCommand } from 'vs/editor/common/controller/cursorTypeOperations';
@@ -17,56 +16,10 @@ import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from 'vs/editor/common/model';
import { RawContentChangedType, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { dispose } from 'vs/base/common/lifecycle';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
function containsLineMappingChanged(events: viewEvents.ViewEvent[]): boolean {
for (let i = 0, len = events.length; i < len; i++) {
if (events[i].type === viewEvents.ViewEventType.ViewLineMappingChanged) {
return true;
}
}
return false;
}
export class CursorStateChangedEvent {
/**
* The new selections.
* The primary selection is always at index 0.
*/
readonly selections: Selection[];
/**
* The new model version id that `selections` apply to.
*/
readonly modelVersionId: number;
/**
* The old selections.
*/
readonly oldSelections: Selection[] | null;
/**
* The model version id the that `oldSelections` apply to.
*/
readonly oldModelVersionId: number;
/**
* Source of the call that caused the event.
*/
readonly source: string;
/**
* Reason.
*/
readonly reason: CursorChangeReason;
constructor(selections: Selection[], modelVersionId: number, oldSelections: Selection[] | null, oldModelVersionId: number, source: string, reason: CursorChangeReason) {
this.selections = selections;
this.modelVersionId = modelVersionId;
this.oldSelections = oldSelections;
this.oldModelVersionId = oldModelVersionId;
this.source = source;
this.reason = reason;
}
}
import { VerticalRevealType, ViewCursorStateChangedEvent, ViewRevealRangeRequestEvent } from 'vs/editor/common/view/viewEvents';
import { dispose, Disposable } from 'vs/base/common/lifecycle';
import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
import { CursorStateChangedEvent, ViewModelEventsCollector } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
/**
* A snapshot of the cursor and the model state
@@ -78,7 +31,7 @@ export class CursorModelState {
constructor(model: ITextModel, cursor: Cursor) {
this.modelVersionId = model.getVersionId();
this.cursorState = cursor.getAll();
this.cursorState = cursor.getCursorStates();
}
public equals(other: CursorModelState | null): boolean {
@@ -166,23 +119,14 @@ class AutoClosedAction {
}
}
export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
export class Cursor extends Disposable {
public static readonly MAX_CURSOR_COUNT = 10000;
private readonly _onDidReachMaxCursorCount: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidReachMaxCursorCount: Event<void> = this._onDidReachMaxCursorCount.event;
private readonly _onDidAttemptReadOnlyEdit: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidAttemptReadOnlyEdit: Event<void> = this._onDidAttemptReadOnlyEdit.event;
private readonly _onDidChange: Emitter<CursorStateChangedEvent> = this._register(new Emitter<CursorStateChangedEvent>());
public readonly onDidChange: Event<CursorStateChangedEvent> = this._onDidChange.event;
private readonly _configuration: editorCommon.IConfiguration;
private readonly _model: ITextModel;
private _knownModelVersionId: number;
private readonly _viewModel: IViewModel;
private readonly _viewModel: ICursorSimpleModel;
private readonly _coordinatesConverter: ICoordinatesConverter;
public context: CursorContext;
private _cursors: CursorCollection;
@@ -194,13 +138,13 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
private _autoClosedActions: AutoClosedAction[];
private _prevEditOperationType: EditOperationType;
constructor(configuration: editorCommon.IConfiguration, model: ITextModel, viewModel: IViewModel) {
constructor(model: ITextModel, viewModel: ICursorSimpleModel, coordinatesConverter: ICoordinatesConverter, cursorConfig: CursorConfiguration) {
super();
this._configuration = configuration;
this._model = model;
this._knownModelVersionId = this._model.getVersionId();
this._viewModel = viewModel;
this.context = new CursorContext(this._configuration, this._model, this._viewModel);
this._coordinatesConverter = coordinatesConverter;
this.context = new CursorContext(this._model, this._coordinatesConverter, cursorConfig);
this._cursors = new CursorCollection(this.context);
this._hasFocus = false;
@@ -210,53 +154,6 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._columnSelectData = null;
this._autoClosedActions = [];
this._prevEditOperationType = EditOperationType.Other;
this._register(this._model.onDidChangeRawContent((e) => {
this._knownModelVersionId = e.versionId;
if (this._isHandling) {
return;
}
this._onModelContentChanged(e);
}));
this._register(viewModel.addEventListener((events: viewEvents.ViewEvent[]) => {
if (!containsLineMappingChanged(events)) {
return;
}
if (this._knownModelVersionId !== this._model.getVersionId()) {
// There are model change events that I didn't yet receive.
//
// This can happen when editing the model, and the view model receives the change events first,
// and the view model emits line mapping changed events, all before the cursor gets a chance to
// recover from markers.
//
// The model change listener above will be called soon and we'll ensure a valid cursor state there.
return;
}
// Ensure valid state
this.setStates('viewModel', CursorChangeReason.NotSet, this.getAll());
}));
const updateCursorContext = () => {
this.context = new CursorContext(this._configuration, this._model, this._viewModel);
this._cursors.updateContext(this.context);
};
this._register(this._model.onDidChangeLanguage((e) => {
updateCursorContext();
}));
this._register(this._model.onDidChangeLanguageConfiguration(() => {
updateCursorContext();
}));
this._register(this._model.onDidChangeOptions(() => {
updateCursorContext();
}));
this._register(this._configuration.onDidChange((e) => {
if (CursorConfiguration.shouldRecreate(e)) {
updateCursorContext();
}
}));
}
public dispose(): void {
@@ -265,6 +162,26 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
super.dispose();
}
public updateConfiguration(cursorConfig: CursorConfiguration): void {
this.context = new CursorContext(this._model, this._coordinatesConverter, cursorConfig);
this._cursors.updateContext(this.context);
}
public onLineMappingChanged(eventsCollector: ViewModelEventsCollector): void {
if (this._knownModelVersionId !== this._model.getVersionId()) {
// There are model change events that I didn't yet receive.
//
// This can happen when editing the model, and the view model receives the change events first,
// and the view model emits line mapping changed events, all before the cursor gets a chance to
// recover from markers.
//
// The model change listener above will be called soon and we'll ensure a valid cursor state there.
return;
}
// Ensure valid state
this.setStates(eventsCollector, 'viewModel', CursorChangeReason.NotSet, this.getCursorStates());
}
public setHasFocus(hasFocus: boolean): void {
this._hasFocus = hasFocus;
}
@@ -285,7 +202,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// ------ some getters/setters
public getPrimaryCursor(): CursorState {
public getPrimaryCursorState(): CursorState {
return this._cursors.getPrimaryCursor();
}
@@ -293,14 +210,15 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._cursors.getLastAddedCursorIndex();
}
public getAll(): CursorState[] {
public getCursorStates(): CursorState[] {
return this._cursors.getAll();
}
public setStates(source: string, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean {
public setStates(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): boolean {
let reachedMaxCursorCount = false;
if (states !== null && states.length > Cursor.MAX_CURSOR_COUNT) {
states = states.slice(0, Cursor.MAX_CURSOR_COUNT);
this._onDidReachMaxCursorCount.fire(undefined);
reachedMaxCursorCount = true;
}
const oldState = new CursorModelState(this._model, this);
@@ -311,25 +229,38 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._validateAutoClosedActions();
return this._emitStateChangedIfNecessary(source, reason, oldState);
return this._emitStateChangedIfNecessary(eventsCollector, source, reason, oldState, reachedMaxCursorCount);
}
public setColumnSelectData(columnSelectData: IColumnSelectData): void {
public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void {
this._columnSelectData = columnSelectData;
}
public reveal(source: string, horizontal: boolean, target: RevealTarget, scrollType: editorCommon.ScrollType): void {
this._revealRange(source, target, viewEvents.VerticalRevealType.Simple, horizontal, scrollType);
public revealPrimary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
const viewPositions = this._cursors.getViewPositions();
if (viewPositions.length > 1) {
this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), VerticalRevealType.Simple, revealHorizontal, scrollType);
return;
} else {
const viewPosition = viewPositions[0];
const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
this._emitCursorRevealRange(eventsCollector, source, viewRange, null, VerticalRevealType.Simple, revealHorizontal, scrollType);
}
}
public revealRange(source: string, revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: editorCommon.ScrollType) {
this.emitCursorRevealRange(source, viewRange, null, verticalType, revealHorizontal, scrollType);
private _revealPrimaryCursor(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
const viewPositions = this._cursors.getViewPositions();
if (viewPositions.length > 1) {
this._emitCursorRevealRange(eventsCollector, source, null, this._cursors.getViewSelections(), verticalType, revealHorizontal, scrollType);
} else {
const viewPosition = viewPositions[0];
const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
this._emitCursorRevealRange(eventsCollector, source, viewRange, null, verticalType, revealHorizontal, scrollType);
}
}
public scrollTo(desiredScrollTop: number): void {
this._viewModel.viewLayout.setScrollPositionSmooth({
scrollTop: desiredScrollTop
});
private _emitCursorRevealRange(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) {
eventsCollector.emitViewEvent(new ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType));
}
public saveState(): editorCommon.ICursorState[] {
@@ -356,7 +287,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return result;
}
public restoreState(states: editorCommon.ICursorState[]): void {
public restoreState(eventsCollector: ViewModelEventsCollector, states: editorCommon.ICursorState[]): void {
let desiredSelections: ISelection[] = [];
@@ -393,11 +324,16 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
});
}
this.setStates('restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections));
this.reveal('restoreState', true, RevealTarget.Primary, editorCommon.ScrollType.Immediate);
this.setStates(eventsCollector, 'restoreState', CursorChangeReason.NotSet, CursorState.fromModelSelections(desiredSelections));
this.revealPrimary(eventsCollector, 'restoreState', true, editorCommon.ScrollType.Immediate);
}
private _onModelContentChanged(e: ModelRawContentChangedEvent): void {
public onModelContentChanged(eventsCollector: ViewModelEventsCollector, e: ModelRawContentChangedEvent): void {
this._knownModelVersionId = e.versionId;
if (this._isHandling) {
return;
}
const hadFlushEvent = e.containsEvent(RawContentChangedType.Flush);
this._prevEditOperationType = EditOperationType.Other;
@@ -407,16 +343,16 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
this._cursors.dispose();
this._cursors = new CursorCollection(this.context);
this._validateAutoClosedActions();
this._emitStateChangedIfNecessary('model', CursorChangeReason.ContentFlush, null);
this._emitStateChangedIfNecessary(eventsCollector, 'model', CursorChangeReason.ContentFlush, null, false);
} else {
if (this._hasFocus && e.resultingSelection && e.resultingSelection.length > 0) {
const cursorState = CursorState.fromModelSelections(e.resultingSelection);
if (this.setStates('modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState)) {
this._revealRange('modelChange', RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
if (this.setStates(eventsCollector, 'modelChange', e.isUndoing ? CursorChangeReason.Undo : e.isRedoing ? CursorChangeReason.Redo : CursorChangeReason.RecoverFromMarkers, cursorState)) {
this._revealPrimaryCursor(eventsCollector, 'modelChange', VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
}
} else {
const selectionsFromMarkers = this._cursors.readSelectionFromMarkers();
this.setStates('modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers));
this.setStates(eventsCollector, 'modelChange', CursorChangeReason.RecoverFromMarkers, CursorState.fromModelSelections(selectionsFromMarkers));
}
}
}
@@ -425,7 +361,15 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._cursors.getPrimaryCursor().modelState.selection;
}
public getColumnSelectData(): IColumnSelectData {
public getTopMostViewPosition(): Position {
return this._cursors.getTopMostViewPosition();
}
public getBottomMostViewPosition(): Position {
return this._cursors.getBottomMostViewPosition();
}
public getCursorColumnSelectData(): IColumnSelectData {
if (this._columnSelectData) {
return this._columnSelectData;
}
@@ -435,9 +379,9 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return {
isReal: false,
fromViewLineNumber: viewSelectionStart.lineNumber,
fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewSelectionStart),
fromViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewSelectionStart),
toViewLineNumber: viewPosition.lineNumber,
toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.config, this.context.viewModel, viewPosition),
toViewVisualColumn: CursorColumns.visibleColumnFromColumn2(this.context.cursorConfig, this._viewModel, viewPosition),
};
}
@@ -445,16 +389,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return this._cursors.getSelections();
}
public getViewSelections(): Selection[] {
return this._cursors.getViewSelections();
}
public getPosition(): Position {
return this._cursors.getPrimaryCursor().modelState.position;
}
public setSelections(source: string, selections: readonly ISelection[]): void {
this.setStates(source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections));
public setSelections(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, selections: readonly ISelection[]): void {
this.setStates(eventsCollector, source, CursorChangeReason.NotSet, CursorState.fromModelSelections(selections));
}
public getPrevEditOperationType(): EditOperationType {
@@ -545,7 +485,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// -----------------------------------------------------------------------------------------------------------
// ----- emitting events
private _emitStateChangedIfNecessary(source: string, reason: CursorChangeReason, oldState: CursorModelState | null): boolean {
private _emitStateChangedIfNecessary(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, reason: CursorChangeReason, oldState: CursorModelState | null, reachedMaxCursorCount: boolean): boolean {
const newState = new CursorModelState(this._model, this);
if (newState.equals(oldState)) {
return false;
@@ -555,12 +495,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
const viewSelections = this._cursors.getViewSelections();
// Let the view get the event first.
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections, selections));
} finally {
this._endEmit();
}
eventsCollector.emitViewEvent(new ViewCursorStateChangedEvent(viewSelections, selections));
// Only after the view has been notified, let the rest of the world know...
if (!oldState
@@ -569,49 +504,12 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
) {
const oldSelections = oldState ? oldState.cursorState.map(s => s.modelState.selection) : null;
const oldModelVersionId = oldState ? oldState.modelVersionId : 0;
this._onDidChange.fire(new CursorStateChangedEvent(selections, newState.modelVersionId, oldSelections, oldModelVersionId, source || 'keyboard', reason));
eventsCollector.emitOutgoingEvent(new CursorStateChangedEvent(oldSelections, selections, oldModelVersionId, newState.modelVersionId, source || 'keyboard', reason, reachedMaxCursorCount));
}
return true;
}
private _revealRange(source: string, revealTarget: RevealTarget, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType): void {
const viewPositions = this._cursors.getViewPositions();
let viewPosition = viewPositions[0];
if (revealTarget === RevealTarget.TopMost) {
for (let i = 1; i < viewPositions.length; i++) {
if (viewPositions[i].isBefore(viewPosition)) {
viewPosition = viewPositions[i];
}
}
} else if (revealTarget === RevealTarget.BottomMost) {
for (let i = 1; i < viewPositions.length; i++) {
if (viewPosition.isBeforeOrEqual(viewPositions[i])) {
viewPosition = viewPositions[i];
}
}
} else {
if (viewPositions.length > 1) {
this.emitCursorRevealRange(source, null, this._cursors.getViewSelections(), verticalType, revealHorizontal, scrollType);
return;
}
}
const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
this.emitCursorRevealRange(source, viewRange, null, verticalType, revealHorizontal, scrollType);
}
public emitCursorRevealRange(source: string, viewRange: Range | null, viewSelections: Selection[] | null, verticalType: viewEvents.VerticalRevealType, revealHorizontal: boolean, scrollType: editorCommon.ScrollType) {
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, viewSelections, verticalType, revealHorizontal, scrollType));
} finally {
this._endEmit();
}
}
// -----------------------------------------------------------------------------------------------------------
// ----- handlers beyond this point
@@ -633,7 +531,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
}
const closeChar = m[1];
const autoClosingPairsCandidates = this.context.config.autoClosingPairsClose2.get(closeChar);
const autoClosingPairsCandidates = this.context.cursorConfig.autoClosingPairsClose2.get(closeChar);
if (!autoClosingPairsCandidates || autoClosingPairsCandidates.length !== 1) {
return null;
}
@@ -651,7 +549,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
return indices;
}
public executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
public executeEdits(eventsCollector: ViewModelEventsCollector, source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
let autoClosingIndices: [number, number][] | null = null;
if (source === 'snippet') {
autoClosingIndices = this._findAutoClosingPairs(edits);
@@ -686,146 +584,117 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
});
if (selections) {
this._isHandling = false;
this.setSelections(source, selections);
this.setSelections(eventsCollector, source, selections);
}
if (autoClosedCharactersRanges.length > 0) {
this._pushAutoClosedAction(autoClosedCharactersRanges, autoClosedEnclosingRanges);
}
}
public trigger(source: string, handlerId: string, payload: any): void {
const H = editorCommon.Handler;
if (handlerId === H.CompositionStart) {
this._isDoingComposition = true;
this._selectionsWhenCompositionStarted = this.getSelections().slice(0);
return;
}
if (handlerId === H.CompositionEnd) {
this._isDoingComposition = false;
}
if (this._configuration.options.get(EditorOption.readOnly)) {
// All the remaining handlers will try to edit the model,
// but we cannot edit when read only...
this._onDidAttemptReadOnlyEdit.fire(undefined);
private _executeEdit(callback: () => void, eventsCollector: ViewModelEventsCollector, source: string | null | undefined, cursorChangeReason: CursorChangeReason = CursorChangeReason.NotSet): void {
if (this.context.cursorConfig.readOnly) {
// we cannot edit when read only...
return;
}
const oldState = new CursorModelState(this._model, this);
let cursorChangeReason = CursorChangeReason.NotSet;
this._cursors.stopTrackingSelections();
// ensure valid state on all cursors
this._cursors.ensureValidState();
this._isHandling = true;
try {
switch (handlerId) {
case H.Type:
this._type(source, <string>payload.text);
break;
case H.ReplacePreviousChar:
this._replacePreviousChar(<string>payload.text, <number>payload.replaceCharCnt);
break;
case H.Paste:
cursorChangeReason = CursorChangeReason.Paste;
this._paste(<string>payload.text, <boolean>payload.pasteOnNewLine, <string[]>payload.multicursorText || []);
break;
case H.Cut:
this._cut();
break;
case H.ExecuteCommand:
this._externalExecuteCommand(<editorCommon.ICommand>payload);
break;
case H.ExecuteCommands:
this._externalExecuteCommands(<editorCommon.ICommand[]>payload);
break;
case H.CompositionEnd:
this._interpretCompositionEnd(source);
break;
}
this._cursors.ensureValidState();
callback();
} catch (err) {
onUnexpectedError(err);
}
this._isHandling = false;
this._cursors.startTrackingSelections();
this._validateAutoClosedActions();
if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) {
this._revealRange(source, RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
if (this._emitStateChangedIfNecessary(eventsCollector, source, cursorChangeReason, oldState, false)) {
this._revealPrimaryCursor(eventsCollector, source, VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth);
}
}
private _interpretCompositionEnd(source: string) {
if (!this._isDoingComposition && source === 'keyboard') {
// composition finishes, let's check if we need to auto complete if necessary.
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters));
this._selectionsWhenCompositionStarted = null;
}
public setIsDoingComposition(isDoingComposition: boolean): void {
this._isDoingComposition = isDoingComposition;
}
private _type(source: string, text: string): void {
if (source === 'keyboard') {
// If this event is coming straight from the keyboard, look for electric characters and enter
public startComposition(eventsCollector: ViewModelEventsCollector): void {
this._selectionsWhenCompositionStarted = this.getSelections().slice(0);
}
const len = text.length;
let offset = 0;
while (offset < len) {
const charLength = strings.nextCharLength(text, offset);
const chr = text.substr(offset, charLength);
// Here we must interpret each typed character individually
public endComposition(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void {
this._executeEdit(() => {
if (source === 'keyboard') {
// composition finishes, let's check if we need to auto complete if necessary.
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr));
offset += charLength;
this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters));
this._selectionsWhenCompositionStarted = null;
}
} else {
this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text));
}
}, eventsCollector, source);
}
private _replacePreviousChar(text: string, replaceCharCnt: number): void {
this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text, replaceCharCnt));
public type(eventsCollector: ViewModelEventsCollector, text: string, source?: string | null | undefined): void {
this._executeEdit(() => {
if (source === 'keyboard') {
// If this event is coming straight from the keyboard, look for electric characters and enter
const len = text.length;
let offset = 0;
while (offset < len) {
const charLength = strings.nextCharLength(text, offset);
const chr = text.substr(offset, charLength);
// Here we must interpret each typed character individually
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), autoClosedCharacters, chr));
offset += charLength;
}
} else {
this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text));
}
}, eventsCollector, source);
}
private _paste(text: string, pasteOnNewLine: boolean, multicursorText: string[]): void {
this._executeEditOperation(TypeOperations.paste(this.context.config, this.context.model, this.getSelections(), text, pasteOnNewLine, multicursorText));
public replacePreviousChar(eventsCollector: ViewModelEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void {
this._executeEdit(() => {
this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replaceCharCnt));
}, eventsCollector, source);
}
private _cut(): void {
this._executeEditOperation(DeleteOperations.cut(this.context.config, this.context.model, this.getSelections()));
public paste(eventsCollector: ViewModelEventsCollector, text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
this._executeEdit(() => {
this._executeEditOperation(TypeOperations.paste(this.context.cursorConfig, this._model, this.getSelections(), text, pasteOnNewLine, multicursorText || []));
}, eventsCollector, source, CursorChangeReason.Paste);
}
private _externalExecuteCommand(command: editorCommon.ICommand): void {
this._cursors.killSecondaryCursors();
this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], {
shouldPushStackElementBefore: false,
shouldPushStackElementAfter: false
}));
public cut(eventsCollector: ViewModelEventsCollector, source?: string | null | undefined): void {
this._executeEdit(() => {
this._executeEditOperation(DeleteOperations.cut(this.context.cursorConfig, this._model, this.getSelections()));
}, eventsCollector, source);
}
private _externalExecuteCommands(commands: editorCommon.ICommand[]): void {
this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, {
shouldPushStackElementBefore: false,
shouldPushStackElementAfter: false
}));
public executeCommand(eventsCollector: ViewModelEventsCollector, command: editorCommon.ICommand, source?: string | null | undefined): void {
this._executeEdit(() => {
this._cursors.killSecondaryCursors();
this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], {
shouldPushStackElementBefore: false,
shouldPushStackElementAfter: false
}));
}, eventsCollector, source);
}
public executeCommands(eventsCollector: ViewModelEventsCollector, commands: editorCommon.ICommand[], source?: string | null | undefined): void {
this._executeEdit(() => {
this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, {
shouldPushStackElementBefore: false,
shouldPushStackElementAfter: false
}));
}, eventsCollector, source);
}
}

View File

@@ -82,6 +82,28 @@ export class CursorCollection {
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;
@@ -204,7 +226,7 @@ export class CursorCollection {
const currentSelection = current.selection;
const nextSelection = next.selection;
if (!this.context.config.multiCursorMergeOverlapping) {
if (!this.context.cursorConfig.multiCursorMergeOverlapping) {
continue;
}

View File

@@ -7,18 +7,16 @@ import { CharCode } from 'vs/base/common/charCode';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as strings from 'vs/base/common/strings';
import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingOvertypeStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ICommand, IConfiguration, ScrollType } from 'vs/editor/common/editorCommon';
import { ICommand, IConfiguration } from 'vs/editor/common/editorCommon';
import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { IAutoClosingPair, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { VerticalRevealType } from 'vs/editor/common/view/viewEvents';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
import { Constants } from 'vs/base/common/uint';
export interface IColumnSelectData {
@@ -46,25 +44,6 @@ export const enum EditOperationType {
DeletingRight = 3
}
export interface ICursors {
readonly context: CursorContext;
getPrimaryCursor(): CursorState;
getLastAddedCursorIndex(): number;
getAll(): CursorState[];
getColumnSelectData(): IColumnSelectData;
setColumnSelectData(columnSelectData: IColumnSelectData): void;
setStates(source: string, reason: CursorChangeReason, states: PartialCursorState[] | null): void;
reveal(source: string, horizontal: boolean, target: RevealTarget, scrollType: ScrollType): void;
revealRange(source: string, revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void;
scrollTo(desiredScrollTop: number): void;
getPrevEditOperationType(): EditOperationType;
setPrevEditOperationType(type: EditOperationType): void;
}
export interface CharacterMap {
[char: string]: string;
}
@@ -357,62 +336,13 @@ export class CursorContext {
_cursorContextBrand: void;
public readonly model: ITextModel;
public readonly viewModel: IViewModel;
public readonly config: CursorConfiguration;
public readonly coordinatesConverter: ICoordinatesConverter;
public readonly cursorConfig: CursorConfiguration;
constructor(configuration: IConfiguration, model: ITextModel, viewModel: IViewModel) {
constructor(model: ITextModel, coordinatesConverter: ICoordinatesConverter, cursorConfig: CursorConfiguration) {
this.model = model;
this.viewModel = viewModel;
this.config = new CursorConfiguration(
this.model.getLanguageIdentifier(),
this.model.getOptions(),
configuration
);
}
public validateViewPosition(viewPosition: Position, modelPosition: Position): Position {
return this.viewModel.coordinatesConverter.validateViewPosition(viewPosition, modelPosition);
}
public validateViewRange(viewRange: Range, expectedModelRange: Range): Range {
return this.viewModel.coordinatesConverter.validateViewRange(viewRange, expectedModelRange);
}
public convertViewRangeToModelRange(viewRange: Range): Range {
return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange);
}
public convertViewPositionToModelPosition(lineNumber: number, column: number): Position {
return this.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, column));
}
public convertModelPositionToViewPosition(modelPosition: Position): Position {
return this.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
}
public convertModelRangeToViewRange(modelRange: Range): Range {
return this.viewModel.coordinatesConverter.convertModelRangeToViewRange(modelRange);
}
public getCurrentScrollTop(): number {
return this.viewModel.viewLayout.getCurrentScrollTop();
}
public getCompletelyVisibleViewRange(): Range {
return this.viewModel.getCompletelyVisibleViewRange();
}
public getCompletelyVisibleModelRange(): Range {
const viewRange = this.viewModel.getCompletelyVisibleViewRange();
return this.viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange);
}
public getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range {
return this.viewModel.getCompletelyVisibleViewRangeAtScrollTop(scrollTop);
}
public getVerticalOffsetForViewLine(viewLineNumber: number): number {
return this.viewModel.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber);
this.coordinatesConverter = coordinatesConverter;
this.cursorConfig = cursorConfig;
}
}

View File

@@ -4,131 +4,132 @@
*--------------------------------------------------------------------------------------------*/
import * as types from 'vs/base/common/types';
import { CursorContext, CursorState, ICursorSimpleModel, PartialCursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon';
import { CursorState, ICursorSimpleModel, PartialCursorState, SingleCursorState } from 'vs/editor/common/controller/cursorCommon';
import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations';
import { WordOperations } from 'vs/editor/common/controller/cursorWordOperations';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
export class CursorMoveCommands {
public static addCursorDown(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
public static addCursorDown(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [], resultLen = 0;
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
if (useLogicalLine) {
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(context.config, context.model, cursor.modelState));
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel.model, cursor.modelState));
} else {
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(context.config, context.viewModel, cursor.viewState));
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel, cursor.viewState));
}
}
return result;
}
public static addCursorUp(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
public static addCursorUp(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [], resultLen = 0;
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
if (useLogicalLine) {
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(context.config, context.model, cursor.modelState));
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel.model, cursor.modelState));
} else {
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(context.config, context.viewModel, cursor.viewState));
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel, cursor.viewState));
}
}
return result;
}
public static moveToBeginningOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
public static moveToBeginningOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = this._moveToLineStart(context, cursor, inSelectionMode);
result[i] = this._moveToLineStart(viewModel, cursor, inSelectionMode);
}
return result;
}
private static _moveToLineStart(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
private static _moveToLineStart(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
const currentViewStateColumn = cursor.viewState.position.column;
const currentModelStateColumn = cursor.modelState.position.column;
const isFirstLineOfWrappedLine = currentViewStateColumn === currentModelStateColumn;
const currentViewStatelineNumber = cursor.viewState.position.lineNumber;
const firstNonBlankColumn = context.viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber);
const firstNonBlankColumn = viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber);
const isBeginningOfViewLine = currentViewStateColumn === firstNonBlankColumn;
if (!isFirstLineOfWrappedLine && !isBeginningOfViewLine) {
return this._moveToLineStartByView(context, cursor, inSelectionMode);
return this._moveToLineStartByView(viewModel, cursor, inSelectionMode);
} else {
return this._moveToLineStartByModel(context, cursor, inSelectionMode);
return this._moveToLineStartByModel(viewModel, cursor, inSelectionMode);
}
}
private static _moveToLineStartByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
private static _moveToLineStartByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromViewState(
MoveOperations.moveToBeginningOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode)
MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)
);
}
private static _moveToLineStartByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
private static _moveToLineStartByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromModelState(
MoveOperations.moveToBeginningOfLine(context.config, context.model, cursor.modelState, inSelectionMode)
MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)
);
}
public static moveToEndOfLine(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
public static moveToEndOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = this._moveToLineEnd(context, cursor, inSelectionMode);
result[i] = this._moveToLineEnd(viewModel, cursor, inSelectionMode);
}
return result;
}
private static _moveToLineEnd(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
private static _moveToLineEnd(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
const viewStatePosition = cursor.viewState.position;
const viewModelMaxColumn = context.viewModel.getLineMaxColumn(viewStatePosition.lineNumber);
const viewModelMaxColumn = viewModel.getLineMaxColumn(viewStatePosition.lineNumber);
const isEndOfViewLine = viewStatePosition.column === viewModelMaxColumn;
const modelStatePosition = cursor.modelState.position;
const modelMaxColumn = context.model.getLineMaxColumn(modelStatePosition.lineNumber);
const modelMaxColumn = viewModel.model.getLineMaxColumn(modelStatePosition.lineNumber);
const isEndLineOfWrappedLine = viewModelMaxColumn - viewStatePosition.column === modelMaxColumn - modelStatePosition.column;
if (isEndOfViewLine || isEndLineOfWrappedLine) {
return this._moveToLineEndByModel(context, cursor, inSelectionMode);
return this._moveToLineEndByModel(viewModel, cursor, inSelectionMode);
} else {
return this._moveToLineEndByView(context, cursor, inSelectionMode);
return this._moveToLineEndByView(viewModel, cursor, inSelectionMode);
}
}
private static _moveToLineEndByView(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
private static _moveToLineEndByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromViewState(
MoveOperations.moveToEndOfLine(context.config, context.viewModel, cursor.viewState, inSelectionMode)
MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)
);
}
private static _moveToLineEndByModel(context: CursorContext, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
private static _moveToLineEndByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
return CursorState.fromModelState(
MoveOperations.moveToEndOfLine(context.config, context.model, cursor.modelState, inSelectionMode)
MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)
);
}
public static expandLineSelection(context: CursorContext, cursors: CursorState[]): PartialCursorState[] {
public static expandLineSelection(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const startLineNumber = cursor.modelState.selection.startLineNumber;
const lineCount = context.model.getLineCount();
const lineCount = viewModel.model.getLineCount();
let endLineNumber = cursor.modelState.selection.endLineNumber;
let endColumn: number;
if (endLineNumber === lineCount) {
endColumn = context.model.getLineMaxColumn(lineCount);
endColumn = viewModel.model.getLineMaxColumn(lineCount);
} else {
endLineNumber++;
endColumn = 1;
@@ -142,27 +143,27 @@ export class CursorMoveCommands {
return result;
}
public static moveToBeginningOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
public static moveToBeginningOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode));
result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));
}
return result;
}
public static moveToEndOfBuffer(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
public static moveToEndOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(context.config, context.model, cursor.modelState, inSelectionMode));
result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));
}
return result;
}
public static selectAll(context: CursorContext, cursor: CursorState): PartialCursorState {
const lineCount = context.model.getLineCount();
const maxColumn = context.model.getLineMaxColumn(lineCount);
public static selectAll(viewModel: IViewModel, cursor: CursorState): PartialCursorState {
const lineCount = viewModel.model.getLineCount();
const maxColumn = viewModel.model.getLineMaxColumn(lineCount);
return CursorState.fromModelState(new SingleCursorState(
new Range(1, 1, 1, 1), 0,
@@ -170,23 +171,23 @@ export class CursorMoveCommands {
));
}
public static line(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
const position = context.model.validatePosition(_position);
public static line(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
const position = viewModel.model.validatePosition(_position);
const viewPosition = (
_viewPosition
? context.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
: context.convertModelPositionToViewPosition(position)
? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
: viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)
);
if (!inSelectionMode || !cursor.modelState.hasSelection()) {
// Entering line selection for the first time
const lineCount = context.model.getLineCount();
const lineCount = viewModel.model.getLineCount();
let selectToLineNumber = position.lineNumber + 1;
let selectToColumn = 1;
if (selectToLineNumber > lineCount) {
selectToLineNumber = lineCount;
selectToColumn = context.model.getLineMaxColumn(selectToLineNumber);
selectToColumn = viewModel.model.getLineMaxColumn(selectToLineNumber);
}
return CursorState.fromModelState(new SingleCursorState(
@@ -206,13 +207,13 @@ export class CursorMoveCommands {
} else if (position.lineNumber > enteringLineNumber) {
const lineCount = context.viewModel.getLineCount();
const lineCount = viewModel.getLineCount();
let selectToViewLineNumber = viewPosition.lineNumber + 1;
let selectToViewColumn = 1;
if (selectToViewLineNumber > lineCount) {
selectToViewLineNumber = lineCount;
selectToViewColumn = context.viewModel.getLineMaxColumn(selectToViewLineNumber);
selectToViewColumn = viewModel.getLineMaxColumn(selectToViewLineNumber);
}
return CursorState.fromViewState(cursor.viewState.move(
@@ -229,12 +230,12 @@ export class CursorMoveCommands {
}
}
public static word(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState {
const position = context.model.validatePosition(_position);
return CursorState.fromModelState(WordOperations.word(context.config, context.model, cursor.modelState, inSelectionMode, position));
public static word(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState {
const position = viewModel.model.validatePosition(_position);
return CursorState.fromModelState(WordOperations.word(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, position));
}
public static cancelSelection(context: CursorContext, cursor: CursorState): PartialCursorState {
public static cancelSelection(viewModel: IViewModel, cursor: CursorState): PartialCursorState {
if (!cursor.modelState.hasSelection()) {
return new CursorState(cursor.modelState, cursor.viewState);
}
@@ -248,108 +249,107 @@ export class CursorMoveCommands {
));
}
public static moveTo(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
const position = context.model.validatePosition(_position);
public static moveTo(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition): PartialCursorState {
const position = viewModel.model.validatePosition(_position);
const viewPosition = (
_viewPosition
? context.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
: context.convertModelPositionToViewPosition(position)
? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
: viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)
);
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0));
}
public static move(context: CursorContext, cursors: CursorState[], args: CursorMove.ParsedArguments): PartialCursorState[] | null {
const inSelectionMode = args.select;
const value = args.value;
switch (args.direction) {
public static simpleMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.SimpleMoveDirection, inSelectionMode: boolean, value: number, unit: CursorMove.Unit): PartialCursorState[] | null {
switch (direction) {
case CursorMove.Direction.Left: {
if (args.unit === CursorMove.Unit.HalfLine) {
if (unit === CursorMove.Unit.HalfLine) {
// Move left by half the current line length
return this._moveHalfLineLeft(context, cursors, inSelectionMode);
return this._moveHalfLineLeft(viewModel, cursors, inSelectionMode);
} else {
// Move left by `moveParams.value` columns
return this._moveLeft(context, cursors, inSelectionMode, value);
return this._moveLeft(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.Right: {
if (args.unit === CursorMove.Unit.HalfLine) {
if (unit === CursorMove.Unit.HalfLine) {
// Move right by half the current line length
return this._moveHalfLineRight(context, cursors, inSelectionMode);
return this._moveHalfLineRight(viewModel, cursors, inSelectionMode);
} else {
// Move right by `moveParams.value` columns
return this._moveRight(context, cursors, inSelectionMode, value);
return this._moveRight(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.Up: {
if (args.unit === CursorMove.Unit.WrappedLine) {
if (unit === CursorMove.Unit.WrappedLine) {
// Move up by view lines
return this._moveUpByViewLines(context, cursors, inSelectionMode, value);
return this._moveUpByViewLines(viewModel, cursors, inSelectionMode, value);
} else {
// Move up by model lines
return this._moveUpByModelLines(context, cursors, inSelectionMode, value);
return this._moveUpByModelLines(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.Down: {
if (args.unit === CursorMove.Unit.WrappedLine) {
if (unit === CursorMove.Unit.WrappedLine) {
// Move down by view lines
return this._moveDownByViewLines(context, cursors, inSelectionMode, value);
return this._moveDownByViewLines(viewModel, cursors, inSelectionMode, value);
} else {
// Move down by model lines
return this._moveDownByModelLines(context, cursors, inSelectionMode, value);
return this._moveDownByModelLines(viewModel, cursors, inSelectionMode, value);
}
}
case CursorMove.Direction.WrappedLineStart: {
// Move to the beginning of the current view line
return this._moveToViewMinColumn(context, cursors, inSelectionMode);
return this._moveToViewMinColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineFirstNonWhitespaceCharacter: {
// Move to the first non-whitespace column of the current view line
return this._moveToViewFirstNonWhitespaceColumn(context, cursors, inSelectionMode);
return this._moveToViewFirstNonWhitespaceColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineColumnCenter: {
// Move to the "center" of the current view line
return this._moveToViewCenterColumn(context, cursors, inSelectionMode);
return this._moveToViewCenterColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineEnd: {
// Move to the end of the current view line
return this._moveToViewMaxColumn(context, cursors, inSelectionMode);
return this._moveToViewMaxColumn(viewModel, cursors, inSelectionMode);
}
case CursorMove.Direction.WrappedLineLastNonWhitespaceCharacter: {
// Move to the last non-whitespace column of the current view line
return this._moveToViewLastNonWhitespaceColumn(context, cursors, inSelectionMode);
return this._moveToViewLastNonWhitespaceColumn(viewModel, cursors, inSelectionMode);
}
}
return null;
}
public static viewportMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null {
const visibleViewRange = viewModel.getCompletelyVisibleViewRange();
const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
switch (direction) {
case CursorMove.Direction.ViewPortTop: {
// Move to the nth line start in the viewport (from the top)
const cursor = cursors[0];
const visibleModelRange = context.getCompletelyVisibleModelRange();
const modelLineNumber = this._firstLineNumberInRange(context.model, visibleModelRange, value);
const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)];
const modelLineNumber = this._firstLineNumberInRange(viewModel.model, visibleModelRange, value);
const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
}
case CursorMove.Direction.ViewPortBottom: {
// Move to the nth line start in the viewport (from the bottom)
const cursor = cursors[0];
const visibleModelRange = context.getCompletelyVisibleModelRange();
const modelLineNumber = this._lastLineNumberInRange(context.model, visibleModelRange, value);
const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)];
const modelLineNumber = this._lastLineNumberInRange(viewModel.model, visibleModelRange, value);
const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
}
case CursorMove.Direction.ViewPortCenter: {
// Move to the line start in the viewport center
const cursor = cursors[0];
const visibleModelRange = context.getCompletelyVisibleModelRange();
const modelLineNumber = Math.round((visibleModelRange.startLineNumber + visibleModelRange.endLineNumber) / 2);
const modelColumn = context.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
return [this._moveToModelPosition(context, cursor, inSelectionMode, modelLineNumber, modelColumn)];
const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
}
case CursorMove.Direction.ViewPortIfOutside: {
// Move to a position inside the viewport
const visibleViewRange = context.getCompletelyVisibleViewRange();
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = this.findPositionInViewportIfOutside(context, cursor, visibleViewRange, inSelectionMode);
result[i] = this.findPositionInViewportIfOutside(viewModel, cursor, visibleViewRange, inSelectionMode);
}
return result;
}
@@ -358,8 +358,7 @@ export class CursorMoveCommands {
return null;
}
public static findPositionInViewportIfOutside(context: CursorContext, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState {
public static findPositionInViewportIfOutside(viewModel: IViewModel, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState {
let viewLineNumber = cursor.viewState.position.lineNumber;
if (visibleViewRange.startLineNumber <= viewLineNumber && viewLineNumber <= visibleViewRange.endLineNumber - 1) {
@@ -373,8 +372,8 @@ export class CursorMoveCommands {
if (viewLineNumber < visibleViewRange.startLineNumber) {
viewLineNumber = visibleViewRange.startLineNumber;
}
const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
return this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
return this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
}
@@ -404,19 +403,19 @@ export class CursorMoveCommands {
return Math.max(startLineNumber, range.endLineNumber - count + 1);
}
private static _moveLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
private static _moveLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
let newViewState = MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns);
let newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
if (!cursor.viewState.hasSelection() && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
// moved over to the previous view line
const newViewModelPosition = context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) {
// stayed on the same model line => pass wrapping point where 2 view positions map to a single model position
newViewState = MoveOperations.moveLeft(context.config, context.viewModel, newViewState, inSelectionMode, 1);
newViewState = MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1);
}
}
@@ -425,29 +424,29 @@ export class CursorMoveCommands {
return result;
}
private static _moveHalfLineLeft(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
private static _moveHalfLineLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2);
result[i] = CursorState.fromViewState(MoveOperations.moveLeft(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine));
const halfLine = Math.round(viewModel.getLineContent(viewLineNumber).length / 2);
result[i] = CursorState.fromViewState(MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));
}
return result;
}
private static _moveRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
private static _moveRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
let newViewState = MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, noOfColumns);
let newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns);
if (!cursor.viewState.hasSelection() && noOfColumns === 1 && newViewState.position.lineNumber !== cursor.viewState.position.lineNumber) {
// moved over to the next view line
const newViewModelPosition = context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
const newViewModelPosition = viewModel.coordinatesConverter.convertViewPositionToModelPosition(newViewState.position);
if (newViewModelPosition.lineNumber === cursor.modelState.position.lineNumber) {
// stayed on the same model line => pass wrapping point where 2 view positions map to a single model position
newViewState = MoveOperations.moveRight(context.config, context.viewModel, newViewState, inSelectionMode, 1);
newViewState = MoveOperations.moveRight(viewModel.cursorConfig, viewModel, newViewState, inSelectionMode, 1);
}
}
@@ -456,112 +455,112 @@ export class CursorMoveCommands {
return result;
}
private static _moveHalfLineRight(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
private static _moveHalfLineRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
const halfLine = Math.round(context.viewModel.getLineContent(viewLineNumber).length / 2);
result[i] = CursorState.fromViewState(MoveOperations.moveRight(context.config, context.viewModel, cursor.viewState, inSelectionMode, halfLine));
const halfLine = Math.round(viewModel.getLineContent(viewLineNumber).length / 2);
result[i] = CursorState.fromViewState(MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));
}
return result;
}
private static _moveDownByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
private static _moveDownByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = CursorState.fromViewState(MoveOperations.moveDown(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount));
result[i] = CursorState.fromViewState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));
}
return result;
}
private static _moveDownByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
private static _moveDownByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = CursorState.fromModelState(MoveOperations.moveDown(context.config, context.model, cursor.modelState, inSelectionMode, linesCount));
result[i] = CursorState.fromModelState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));
}
return result;
}
private static _moveUpByViewLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
private static _moveUpByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = CursorState.fromViewState(MoveOperations.moveUp(context.config, context.viewModel, cursor.viewState, inSelectionMode, linesCount));
result[i] = CursorState.fromViewState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));
}
return result;
}
private static _moveUpByModelLines(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
private static _moveUpByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[i] = CursorState.fromModelState(MoveOperations.moveUp(context.config, context.model, cursor.modelState, inSelectionMode, linesCount));
result[i] = CursorState.fromModelState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));
}
return result;
}
private static _moveToViewPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState {
private static _moveToViewPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState {
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, toViewLineNumber, toViewColumn, 0));
}
private static _moveToModelPosition(context: CursorContext, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState {
private static _moveToModelPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState {
return CursorState.fromModelState(cursor.modelState.move(inSelectionMode, toModelLineNumber, toModelColumn, 0));
}
private static _moveToViewMinColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
private static _moveToViewMinColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
const viewColumn = context.viewModel.getLineMinColumn(viewLineNumber);
result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
const viewColumn = viewModel.getLineMinColumn(viewLineNumber);
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
private static _moveToViewFirstNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
private static _moveToViewFirstNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
const viewColumn = context.viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
private static _moveToViewCenterColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
private static _moveToViewCenterColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
const viewColumn = Math.round((context.viewModel.getLineMaxColumn(viewLineNumber) + context.viewModel.getLineMinColumn(viewLineNumber)) / 2);
result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
const viewColumn = Math.round((viewModel.getLineMaxColumn(viewLineNumber) + viewModel.getLineMinColumn(viewLineNumber)) / 2);
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
private static _moveToViewMaxColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
private static _moveToViewMaxColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
const viewColumn = context.viewModel.getLineMaxColumn(viewLineNumber);
result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
const viewColumn = viewModel.getLineMaxColumn(viewLineNumber);
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
private static _moveToViewLastNonWhitespaceColumn(context: CursorContext, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
private static _moveToViewLastNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
let result: PartialCursorState[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
const viewLineNumber = cursor.viewState.position.lineNumber;
const viewColumn = context.viewModel.getLineLastNonWhitespaceColumn(viewLineNumber);
result[i] = this._moveToViewPosition(context, cursor, inSelectionMode, viewLineNumber, viewColumn);
const viewColumn = viewModel.getLineLastNonWhitespaceColumn(viewLineNumber);
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
}
return result;
}
@@ -767,6 +766,13 @@ export namespace CursorMove {
value: number;
}
export interface SimpleMoveArguments {
direction: SimpleMoveDirection;
unit: Unit;
select: boolean;
value: number;
}
export const enum Direction {
Left,
Right,
@@ -786,6 +792,25 @@ export namespace CursorMove {
ViewPortIfOutside,
}
export type SimpleMoveDirection = (
Direction.Left
| Direction.Right
| Direction.Up
| Direction.Down
| Direction.WrappedLineStart
| Direction.WrappedLineFirstNonWhitespaceCharacter
| Direction.WrappedLineColumnCenter
| Direction.WrappedLineEnd
| Direction.WrappedLineLastNonWhitespaceCharacter
);
export type ViewportDirection = (
Direction.ViewPortTop
| Direction.ViewPortCenter
| Direction.ViewPortBottom
| Direction.ViewPortIfOutside
);
export const enum Unit {
None,
Line,

View File

@@ -81,11 +81,11 @@ export class OneCursor {
}
// We only have the view state => compute the model state
const selectionStart = context.model.validateRange(
context.convertViewRangeToModelRange(viewState.selectionStart)
context.coordinatesConverter.convertViewRangeToModelRange(viewState.selectionStart)
);
const position = context.model.validatePosition(
context.convertViewPositionToModelPosition(viewState.position.lineNumber, viewState.position.column)
context.coordinatesConverter.convertViewPositionToModelPosition(viewState.position)
);
modelState = new SingleCursorState(selectionStart, viewState.selectionStartLeftoverVisibleColumns, position, viewState.leftoverVisibleColumns);
@@ -104,15 +104,15 @@ export class OneCursor {
if (!viewState) {
// We only have the model state => compute the view state
const viewSelectionStart1 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn));
const viewSelectionStart2 = context.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn));
const viewSelectionStart1 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.startLineNumber, modelState.selectionStart.startColumn));
const viewSelectionStart2 = context.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelState.selectionStart.endLineNumber, modelState.selectionStart.endColumn));
const viewSelectionStart = new Range(viewSelectionStart1.lineNumber, viewSelectionStart1.column, viewSelectionStart2.lineNumber, viewSelectionStart2.column);
const viewPosition = context.convertModelPositionToViewPosition(modelState.position);
const viewPosition = context.coordinatesConverter.convertModelPositionToViewPosition(modelState.position);
viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns);
} else {
// Validate new view state
const viewSelectionStart = context.validateViewRange(viewState.selectionStart, modelState.selectionStart);
const viewPosition = context.validateViewPosition(viewState.position, modelState.position);
const viewSelectionStart = context.coordinatesConverter.validateViewRange(viewState.selectionStart, modelState.selectionStart);
const viewPosition = context.coordinatesConverter.validateViewPosition(viewState.position, modelState.position);
viewState = new SingleCursorState(viewSelectionStart, modelState.selectionStartLeftoverVisibleColumns, viewPosition, modelState.leftoverVisibleColumns);
}

View File

@@ -150,6 +150,7 @@ export interface ILineChange extends IChange {
* @internal
*/
export interface IConfiguration extends IDisposable {
onDidChangeFast(listener: (e: ConfigurationChangedEvent) => void): IDisposable;
onDidChange(listener: (e: ConfigurationChangedEvent) => void): IDisposable;
readonly options: IComputedEditorOptions;
@@ -480,7 +481,7 @@ export interface IEditor {
* @param handlerId The id of the handler or the id of a contribution.
* @param payload Extra data to be sent to the handler.
*/
trigger(source: string, handlerId: string, payload: any): void;
trigger(source: string | null | undefined, handlerId: string, payload: any): void;
/**
* Gets the current model attached to this editor.
@@ -688,14 +689,36 @@ export const EditorType = {
* Built-in commands.
* @internal
*/
export const Handler = {
ExecuteCommand: 'executeCommand',
ExecuteCommands: 'executeCommands',
export const enum Handler {
CompositionStart = 'compositionStart',
CompositionEnd = 'compositionEnd',
Type = 'type',
ReplacePreviousChar = 'replacePreviousChar',
Paste = 'paste',
Cut = 'cut',
}
Type: 'type',
ReplacePreviousChar: 'replacePreviousChar',
CompositionStart: 'compositionStart',
CompositionEnd: 'compositionEnd',
Paste: 'paste',
Cut: 'cut',
};
/**
* @internal
*/
export interface TypePayload {
text: string;
}
/**
* @internal
*/
export interface ReplacePreviousCharPayload {
text: string;
replaceCharCnt: number;
}
/**
* @internal
*/
export interface PastePayload {
text: string;
pasteOnNewLine: boolean;
multicursorText: string[] | null;
mode: string | null;
}

View File

@@ -551,6 +551,18 @@ export interface ITextModel {
*/
mightContainRTL(): boolean;
/**
* If true, the text model might contain LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS), NEXT LINE (NEL).
* If false, the text model definitely does not contain these.
* @internal
*/
mightContainUnusualLineTerminators(): boolean;
/**
* @internal
*/
removeUnusualLineTerminators(selections?: Selection[]): void;
/**
* If true, the text model might contain non basic ASCII.
* If false, the text model **contains only** basic ASCII.
@@ -1281,6 +1293,8 @@ export interface IReadonlyTextBuffer {
onDidChangeContent: Event<void>;
equals(other: ITextBuffer): boolean;
mightContainRTL(): boolean;
mightContainUnusualLineTerminators(): boolean;
resetMightContainUnusualLineTerminators(): void;
mightContainNonBasicASCII(): boolean;
getBOM(): string;
getEOL(): string;

View File

@@ -12,6 +12,7 @@ import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorks
import { URI } from 'vs/base/common/uri';
import { TextChange, compressConsecutiveTextChanges } from 'vs/editor/common/model/textChange';
import * as buffer from 'vs/base/common/buffer';
import { IDisposable } from 'vs/base/common/lifecycle';
function uriGetComparisonKey(resource: URI): string {
return resource.toString();
@@ -138,6 +139,10 @@ class SingleModelEditStackData {
}
}
export interface IUndoRedoDelegate {
prepareUndoRedo(element: MultiModelEditStackElement): Promise<IDisposable> | IDisposable | void;
}
export class SingleModelEditStackElement implements IResourceUndoRedoElement {
public model: ITextModel | URI;
@@ -224,6 +229,8 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
private readonly _editStackElementsArr: SingleModelEditStackElement[];
private readonly _editStackElementsMap: Map<string, SingleModelEditStackElement>;
private _delegate: IUndoRedoDelegate | null;
public get resources(): readonly URI[] {
return this._editStackElementsArr.map(editStackElement => editStackElement.resource);
}
@@ -240,6 +247,27 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
const key = uriGetComparisonKey(editStackElement.resource);
this._editStackElementsMap.set(key, editStackElement);
}
this._delegate = null;
}
public setDelegate(delegate: IUndoRedoDelegate): void {
this._delegate = delegate;
}
public prepareUndoRedo(): Promise<IDisposable> | IDisposable | void {
if (this._delegate) {
return this._delegate.prepareUndoRedo(this);
}
}
public getMissingModels(): URI[] {
const result: URI[] = [];
for (const editStackElement of this._editStackElementsArr) {
if (URI.isUri(editStackElement.model)) {
result.push(editStackElement.model);
}
}
return result;
}
public setModel(model: ITextModel | URI): void {

View File

@@ -36,15 +36,17 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
private readonly _pieceTree: PieceTreeBase;
private readonly _BOM: string;
private _mightContainRTL: boolean;
private _mightContainUnusualLineTerminators: boolean;
private _mightContainNonBasicASCII: boolean;
private readonly _onDidChangeContent: Emitter<void> = new Emitter<void>();
public readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
constructor(chunks: StringBuffer[], BOM: string, eol: '\r\n' | '\n', containsRTL: boolean, isBasicASCII: boolean, eolNormalized: boolean) {
constructor(chunks: StringBuffer[], BOM: string, eol: '\r\n' | '\n', containsRTL: boolean, containsUnusualLineTerminators: boolean, isBasicASCII: boolean, eolNormalized: boolean) {
this._BOM = BOM;
this._mightContainNonBasicASCII = !isBasicASCII;
this._mightContainRTL = containsRTL;
this._mightContainUnusualLineTerminators = containsUnusualLineTerminators;
this._pieceTree = new PieceTreeBase(chunks, eol, eolNormalized);
}
dispose(): void {
@@ -67,6 +69,12 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
public mightContainRTL(): boolean {
return this._mightContainRTL;
}
public mightContainUnusualLineTerminators(): boolean {
return this._mightContainUnusualLineTerminators;
}
public resetMightContainUnusualLineTerminators(): void {
this._mightContainUnusualLineTerminators = false;
}
public mightContainNonBasicASCII(): boolean {
return this._mightContainNonBasicASCII;
}
@@ -216,6 +224,7 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
public applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult {
let mightContainRTL = this._mightContainRTL;
let mightContainUnusualLineTerminators = this._mightContainUnusualLineTerminators;
let mightContainNonBasicASCII = this._mightContainNonBasicASCII;
let canReduceOperations = true;
@@ -226,12 +235,20 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
canReduceOperations = false;
}
let validatedRange = op.range;
if (!mightContainRTL && op.text) {
// check if the new inserted text contains RTL
mightContainRTL = strings.containsRTL(op.text);
}
if (!mightContainNonBasicASCII && op.text) {
mightContainNonBasicASCII = !strings.isBasicASCII(op.text);
if (op.text) {
let textMightContainNonBasicASCII = true;
if (!mightContainNonBasicASCII) {
textMightContainNonBasicASCII = !strings.isBasicASCII(op.text);
mightContainNonBasicASCII = textMightContainNonBasicASCII;
}
if (!mightContainRTL && textMightContainNonBasicASCII) {
// check if the new inserted text contains RTL
mightContainRTL = strings.containsRTL(op.text);
}
if (!mightContainUnusualLineTerminators && textMightContainNonBasicASCII) {
// check if the new inserted text contains unusual line terminators
mightContainUnusualLineTerminators = strings.containsUnusualLineTerminators(op.text);
}
}
let validText = '';
@@ -340,6 +357,7 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
this._mightContainRTL = mightContainRTL;
this._mightContainUnusualLineTerminators = mightContainUnusualLineTerminators;
this._mightContainNonBasicASCII = mightContainNonBasicASCII;
const contentChanges = this._doApplyEdits(operations);

View File

@@ -18,6 +18,7 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory {
private readonly _lf: number,
private readonly _crlf: number,
private readonly _containsRTL: boolean,
private readonly _containsUnusualLineTerminators: boolean,
private readonly _isBasicASCII: boolean,
private readonly _normalizeEOL: boolean
) { }
@@ -53,7 +54,7 @@ export class PieceTreeTextBufferFactory implements ITextBufferFactory {
}
}
return new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._isBasicASCII, this._normalizeEOL);
return new PieceTreeTextBuffer(chunks, this._bom, eol, this._containsRTL, this._containsUnusualLineTerminators, this._isBasicASCII, this._normalizeEOL);
}
public getFirstLineText(lengthLimit: number): string {
@@ -73,6 +74,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder {
private lf: number;
private crlf: number;
private containsRTL: boolean;
private containsUnusualLineTerminators: boolean;
private isBasicASCII: boolean;
constructor() {
@@ -87,6 +89,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder {
this.lf = 0;
this.crlf = 0;
this.containsRTL = false;
this.containsUnusualLineTerminators = false;
this.isBasicASCII = true;
}
@@ -140,9 +143,13 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder {
this.isBasicASCII = lineStarts.isBasicASCII;
}
if (!this.isBasicASCII && !this.containsRTL) {
// No need to check if is basic ASCII
// No need to check if it is basic ASCII
this.containsRTL = strings.containsRTL(chunk);
}
if (!this.isBasicASCII && !this.containsUnusualLineTerminators) {
// No need to check if it is basic ASCII
this.containsUnusualLineTerminators = strings.containsUnusualLineTerminators(chunk);
}
}
public finish(normalizeEOL: boolean = true): PieceTreeTextBufferFactory {
@@ -154,6 +161,7 @@ export class PieceTreeTextBufferBuilder implements ITextBufferBuilder {
this.lf,
this.crlf,
this.containsRTL,
this.containsUnusualLineTerminators,
this.isBasicASCII,
normalizeEOL
);

View File

@@ -37,6 +37,7 @@ import { Color } from 'vs/base/common/color';
import { EditorTheme } from 'vs/editor/common/view/viewContext';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { TextChange } from 'vs/editor/common/model/textChange';
import { Constants } from 'vs/base/common/uint';
function createTextBufferBuilder() {
return new PieceTreeTextBufferBuilder();
@@ -699,6 +700,17 @@ export class TextModel extends Disposable implements model.ITextModel {
return this._buffer.mightContainRTL();
}
public mightContainUnusualLineTerminators(): boolean {
return this._buffer.mightContainUnusualLineTerminators();
}
public removeUnusualLineTerminators(selections: Selection[] | null = null): void {
const matches = this.findMatches(strings.UNUSUAL_LINE_TERMINATORS.source, false, true, false, null, false, Constants.MAX_SAFE_SMALL_INTEGER);
const eol = this.getEOL();
this._buffer.resetMightContainUnusualLineTerminators();
this.pushEditOperations(selections, matches.map(m => ({ range: m.range, text: eol })), () => null);
}
public mightContainNonBasicASCII(): boolean {
return this._buffer.mightContainNonBasicASCII();
}
@@ -1097,7 +1109,7 @@ export class TextModel extends Disposable implements model.ITextModel {
return this._buffer.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
}
public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] {
public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] {
this._assertNotDisposed();
let searchRange: Range;

View File

@@ -1411,11 +1411,12 @@ export interface RenameProvider {
*/
export interface AuthenticationSession {
id: string;
getAccessToken(): Thenable<string>;
accessToken: string;
account: {
displayName: string;
id: string;
}
scopes: string[];
}
/**
@@ -1602,7 +1603,7 @@ export interface IWebviewPortMapping {
export interface IWebviewOptions {
readonly enableScripts?: boolean;
readonly enableCommandUris?: boolean;
readonly localResourceRoots?: ReadonlyArray<URI>;
readonly localResourceRoots?: ReadonlyArray<UriComponents>;
readonly portMapping?: ReadonlyArray<IWebviewPortMapping>;
}

View File

@@ -68,7 +68,9 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens
break;
case CharCode.UTF8_BOM:
case CharCode.LINE_SEPARATOR_2028:
case CharCode.LINE_SEPARATOR:
case CharCode.PARAGRAPH_SEPARATOR:
case CharCode.NEXT_LINE:
partContent += '\ufffd';
break;

View File

@@ -113,12 +113,12 @@ interface IRawConfig {
const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLine.LF : DefaultEndOfLine.CRLF;
interface EditStackPastFutureElements {
export interface EditStackPastFutureElements {
past: EditStackElement[];
future: EditStackElement[];
}
function isEditStackPastFutureElements(undoElements: IPastFutureElements): undoElements is EditStackPastFutureElements {
export function isEditStackPastFutureElements(undoElements: IPastFutureElements): undoElements is EditStackPastFutureElements {
return (isEditStackElements(undoElements.past) && isEditStackElements(undoElements.future));
}

View File

@@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
import { isEditStackPastFutureElements } from 'vs/editor/common/services/modelServiceImpl';
import { IUndoRedoDelegate, MultiModelEditStackElement } from 'vs/editor/common/model/editStack';
export class ModelUndoRedoParticipant extends Disposable implements IUndoRedoDelegate {
constructor(
@IModelService private readonly _modelService: IModelService,
@ITextModelService private readonly _textModelService: ITextModelService,
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
) {
super();
this._register(this._modelService.onModelRemoved((model) => {
// a model will get disposed, so let's check if the undo redo stack is maintained
const elements = this._undoRedoService.getElements(model.uri);
if (elements.past.length === 0 && elements.future.length === 0) {
return;
}
if (!isEditStackPastFutureElements(elements)) {
return;
}
for (const element of elements.past) {
if (element.type === UndoRedoElementType.Workspace) {
element.setDelegate(this);
}
}
for (const element of elements.future) {
if (element.type === UndoRedoElementType.Workspace) {
element.setDelegate(this);
}
}
}));
}
public prepareUndoRedo(element: MultiModelEditStackElement): IDisposable | Promise<IDisposable> {
// Load all the needed text models
const missingModels = element.getMissingModels();
if (missingModels.length === 0) {
// All models are available!
return Disposable.None;
}
const disposablesPromises = missingModels.map(async (uri) => {
try {
const reference = await this._textModelService.createModelReference(uri);
return <IDisposable>reference;
} catch (err) {
// This model could not be loaded, maybe it was deleted in the meantime?
return Disposable.None;
}
});
return Promise.all(disposablesPromises).then(disposables => {
return {
dispose: () => dispose(disposables)
};
});
}
}

View File

@@ -26,9 +26,9 @@ export interface ITextModelService {
registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable;
/**
* Check if a provider for the given `scheme` exists
* Check if the given resource can be resolved to a text model.
*/
hasTextModelContentProvider(scheme: string): boolean;
canHandleResource(resource: URI): boolean;
}
export interface ITextModelContentProvider {

View File

@@ -240,49 +240,50 @@ export enum EditorOption {
quickSuggestions = 70,
quickSuggestionsDelay = 71,
readOnly = 72,
renameOnType = 73,
renderControlCharacters = 74,
renderIndentGuides = 75,
renderFinalNewline = 76,
renderLineHighlight = 77,
renderLineHighlightOnlyWhenFocus = 78,
renderValidationDecorations = 79,
renderWhitespace = 80,
revealHorizontalRightPadding = 81,
roundedSelection = 82,
rulers = 83,
scrollbar = 84,
scrollBeyondLastColumn = 85,
scrollBeyondLastLine = 86,
scrollPredominantAxis = 87,
selectionClipboard = 88,
selectionHighlight = 89,
selectOnLineNumbers = 90,
showFoldingControls = 91,
showUnused = 92,
snippetSuggestions = 93,
smoothScrolling = 94,
stopRenderingLineAfter = 95,
suggest = 96,
suggestFontSize = 97,
suggestLineHeight = 98,
suggestOnTriggerCharacters = 99,
suggestSelection = 100,
tabCompletion = 101,
useTabStops = 102,
wordSeparators = 103,
wordWrap = 104,
wordWrapBreakAfterCharacters = 105,
wordWrapBreakBeforeCharacters = 106,
wordWrapColumn = 107,
wordWrapMinified = 108,
wrappingIndent = 109,
wrappingStrategy = 110,
editorClassName = 111,
pixelRatio = 112,
tabFocusMode = 113,
layoutInfo = 114,
wrappingInfo = 115
removeUnusualLineTerminators = 73,
renameOnType = 74,
renderControlCharacters = 75,
renderIndentGuides = 76,
renderFinalNewline = 77,
renderLineHighlight = 78,
renderLineHighlightOnlyWhenFocus = 79,
renderValidationDecorations = 80,
renderWhitespace = 81,
revealHorizontalRightPadding = 82,
roundedSelection = 83,
rulers = 84,
scrollbar = 85,
scrollBeyondLastColumn = 86,
scrollBeyondLastLine = 87,
scrollPredominantAxis = 88,
selectionClipboard = 89,
selectionHighlight = 90,
selectOnLineNumbers = 91,
showFoldingControls = 92,
showUnused = 93,
snippetSuggestions = 94,
smoothScrolling = 95,
stopRenderingLineAfter = 96,
suggest = 97,
suggestFontSize = 98,
suggestLineHeight = 99,
suggestOnTriggerCharacters = 100,
suggestSelection = 101,
tabCompletion = 102,
useTabStops = 103,
wordSeparators = 104,
wordWrap = 105,
wordWrapBreakAfterCharacters = 106,
wordWrapBreakBeforeCharacters = 107,
wordWrapColumn = 108,
wordWrapMinified = 109,
wrappingIndent = 110,
wrappingStrategy = 111,
editorClassName = 112,
pixelRatio = 113,
tabFocusMode = 114,
layoutInfo = 115,
wrappingInfo = 116
}
/**

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { IConfiguration } from 'vs/editor/common/editorCommon';
import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher';
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
import { IViewLayout, IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { IColorTheme, ThemeType } from 'vs/platform/theme/common/themeService';
@@ -37,27 +36,24 @@ export class ViewContext {
public readonly configuration: IConfiguration;
public readonly model: IViewModel;
public readonly viewLayout: IViewLayout;
public readonly privateViewEventBus: ViewEventDispatcher;
public readonly theme: EditorTheme;
constructor(
configuration: IConfiguration,
theme: IColorTheme,
model: IViewModel,
privateViewEventBus: ViewEventDispatcher
model: IViewModel
) {
this.configuration = configuration;
this.theme = new EditorTheme(theme);
this.model = model;
this.viewLayout = model.viewLayout;
this.privateViewEventBus = privateViewEventBus;
}
public addEventHandler(eventHandler: ViewEventHandler): void {
this.privateViewEventBus.addEventHandler(eventHandler);
this.model.addViewEventHandler(eventHandler);
}
public removeEventHandler(eventHandler: ViewEventHandler): void {
this.privateViewEventBus.removeEventHandler(eventHandler);
this.model.removeViewEventHandler(eventHandler);
}
}

View File

@@ -1,92 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ViewEvent } from 'vs/editor/common/view/viewEvents';
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
export class ViewEventDispatcher {
private readonly _eventHandlerGateKeeper: (callback: () => void) => void;
private readonly _eventHandlers: ViewEventHandler[];
private _eventQueue: ViewEvent[] | null;
private _isConsumingQueue: boolean;
constructor(eventHandlerGateKeeper: (callback: () => void) => void) {
this._eventHandlerGateKeeper = eventHandlerGateKeeper;
this._eventHandlers = [];
this._eventQueue = null;
this._isConsumingQueue = false;
}
public addEventHandler(eventHandler: ViewEventHandler): void {
for (let i = 0, len = this._eventHandlers.length; i < len; i++) {
if (this._eventHandlers[i] === eventHandler) {
console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler);
}
}
this._eventHandlers.push(eventHandler);
}
public removeEventHandler(eventHandler: ViewEventHandler): void {
for (let i = 0; i < this._eventHandlers.length; i++) {
if (this._eventHandlers[i] === eventHandler) {
this._eventHandlers.splice(i, 1);
break;
}
}
}
public emit(event: ViewEvent): void {
if (this._eventQueue) {
this._eventQueue.push(event);
} else {
this._eventQueue = [event];
}
if (!this._isConsumingQueue) {
this.consumeQueue();
}
}
public emitMany(events: ViewEvent[]): void {
if (this._eventQueue) {
this._eventQueue = this._eventQueue.concat(events);
} else {
this._eventQueue = events;
}
if (!this._isConsumingQueue) {
this.consumeQueue();
}
}
private consumeQueue(): void {
this._eventHandlerGateKeeper(() => {
try {
this._isConsumingQueue = true;
this._doConsumeQueue();
} finally {
this._isConsumingQueue = false;
}
});
}
private _doConsumeQueue(): void {
while (this._eventQueue) {
// Empty event queue, as events might come in while sending these off
let events = this._eventQueue;
this._eventQueue = null;
// Use a clone of the event handlers list, as they might remove themselves
let eventHandlers = this._eventHandlers.slice(0);
for (let i = 0, len = eventHandlers.length; i < len; i++) {
eventHandlers[i].handleEvents(events);
}
}
}
}

View File

@@ -3,33 +3,30 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as errors from 'vs/base/common/errors';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
export const enum ViewEventType {
ViewConfigurationChanged = 1,
ViewContentSizeChanged = 2,
ViewCursorStateChanged = 3,
ViewDecorationsChanged = 4,
ViewFlushed = 5,
ViewFocusChanged = 6,
ViewLanguageConfigurationChanged = 7,
ViewLineMappingChanged = 8,
ViewLinesChanged = 9,
ViewLinesDeleted = 10,
ViewLinesInserted = 11,
ViewRevealRangeRequest = 12,
ViewScrollChanged = 13,
ViewThemeChanged = 14,
ViewTokensChanged = 15,
ViewTokensColorsChanged = 16,
ViewZonesChanged = 17,
ViewConfigurationChanged,
ViewCursorStateChanged,
ViewDecorationsChanged,
ViewFlushed,
ViewFocusChanged,
ViewLanguageConfigurationChanged,
ViewLineMappingChanged,
ViewLinesChanged,
ViewLinesDeleted,
ViewLinesInserted,
ViewRevealRangeRequest,
ViewScrollChanged,
ViewThemeChanged,
ViewTokensChanged,
ViewTokensColorsChanged,
ViewZonesChanged,
}
export class ViewConfigurationChangedEvent {
@@ -47,25 +44,6 @@ export class ViewConfigurationChangedEvent {
}
}
export class ViewContentSizeChangedEvent implements IContentSizeChangedEvent {
public readonly type = ViewEventType.ViewContentSizeChanged;
public readonly contentWidth: number;
public readonly contentHeight: number;
public readonly contentWidthChanged: boolean;
public readonly contentHeightChanged: boolean;
constructor(source: IContentSizeChangedEvent) {
this.contentWidth = source.contentWidth;
this.contentHeight = source.contentHeight;
this.contentWidthChanged = source.contentWidthChanged;
this.contentHeightChanged = source.contentHeightChanged;
}
}
export class ViewCursorStateChangedEvent {
public readonly type = ViewEventType.ViewCursorStateChanged;
@@ -224,9 +202,9 @@ export class ViewRevealRangeRequestEvent {
/**
* Source of the call that caused the event.
*/
readonly source: string;
readonly source: string | null | undefined;
constructor(source: string, range: Range | null, selections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: ScrollType) {
constructor(source: string | null | undefined, range: Range | null, selections: Selection[] | null, verticalType: VerticalRevealType, revealHorizontal: boolean, scrollType: ScrollType) {
this.source = source;
this.range = range;
this.selections = selections;
@@ -308,7 +286,6 @@ export class ViewZonesChangedEvent {
export type ViewEvent = (
ViewConfigurationChangedEvent
| ViewContentSizeChangedEvent
| ViewCursorStateChangedEvent
| ViewDecorationsChangedEvent
| ViewFlushedEvent
@@ -325,94 +302,3 @@ export type ViewEvent = (
| ViewTokensColorsChangedEvent
| ViewZonesChangedEvent
);
export interface IViewEventListener {
(events: ViewEvent[]): void;
}
export class ViewEventEmitter extends Disposable {
private _listeners: IViewEventListener[];
private _collector: ViewEventsCollector | null;
private _collectorCnt: number;
constructor() {
super();
this._listeners = [];
this._collector = null;
this._collectorCnt = 0;
}
public dispose(): void {
this._listeners = [];
super.dispose();
}
protected _beginEmit(): ViewEventsCollector {
this._collectorCnt++;
if (this._collectorCnt === 1) {
this._collector = new ViewEventsCollector();
}
return this._collector!;
}
protected _endEmit(): void {
this._collectorCnt--;
if (this._collectorCnt === 0) {
const events = this._collector!.finalize();
this._collector = null;
if (events.length > 0) {
this._emit(events);
}
}
}
private _emit(events: ViewEvent[]): void {
const listeners = this._listeners.slice(0);
for (let i = 0, len = listeners.length; i < len; i++) {
safeInvokeListener(listeners[i], events);
}
}
public addEventListener(listener: (events: ViewEvent[]) => void): IDisposable {
this._listeners.push(listener);
return toDisposable(() => {
let listeners = this._listeners;
for (let i = 0, len = listeners.length; i < len; i++) {
if (listeners[i] === listener) {
listeners.splice(i, 1);
break;
}
}
});
}
}
export class ViewEventsCollector {
private _events: ViewEvent[];
private _eventsLen = 0;
constructor() {
this._events = [];
this._eventsLen = 0;
}
public emit(event: ViewEvent) {
this._events[this._eventsLen++] = event;
}
public finalize(): ViewEvent[] {
let result = this._events;
this._events = [];
return result;
}
}
function safeInvokeListener(listener: IViewEventListener, events: ViewEvent[]): void {
try {
listener(events);
} catch (e) {
errors.onUnexpectedError(e);
}
}

View File

@@ -180,33 +180,36 @@ export class LinesLayout {
this._lineCount = lineCount;
}
public changeWhitespace<T>(callback: (accessor: IWhitespaceChangeAccessor) => T): T {
public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean {
let hadAChange = false;
try {
const accessor = {
const accessor: IWhitespaceChangeAccessor = {
insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => {
hadAChange = true;
afterLineNumber = afterLineNumber | 0;
ordinal = ordinal | 0;
heightInPx = heightInPx | 0;
minWidth = minWidth | 0;
const id = this._instanceId + (++this._lastWhitespaceId);
this._pendingChanges.insert(new EditorWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth));
return id;
},
changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): void => {
hadAChange = true;
newAfterLineNumber = newAfterLineNumber | 0;
newHeight = newHeight | 0;
this._pendingChanges.change({ id, newAfterLineNumber, newHeight });
},
removeWhitespace: (id: string): void => {
hadAChange = true;
this._pendingChanges.remove({ id });
}
};
return callback(accessor);
callback(accessor);
} finally {
this._pendingChanges.commit(this);
}
return hadAChange;
}
public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void {

View File

@@ -7,10 +7,11 @@ import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable';
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
import { IConfiguration, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon';
import { LinesLayout, IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel';
import { ContentSizeChangedEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
const SMOOTH_SCROLLING_TIME = 125;
@@ -75,8 +76,8 @@ class EditorScrollable extends Disposable {
public readonly onDidScroll: Event<ScrollEvent>;
private readonly _onDidContentSizeChange = this._register(new Emitter<IContentSizeChangedEvent>());
public readonly onDidContentSizeChange: Event<IContentSizeChangedEvent> = this._onDidContentSizeChange.event;
private readonly _onDidContentSizeChange = this._register(new Emitter<ContentSizeChangedEvent>());
public readonly onDidContentSizeChange: Event<ContentSizeChangedEvent> = this._onDidContentSizeChange.event;
constructor(smoothScrollDuration: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) {
super();
@@ -119,13 +120,10 @@ class EditorScrollable extends Disposable {
const contentWidthChanged = (oldDimensions.contentWidth !== dimensions.contentWidth);
const contentHeightChanged = (oldDimensions.contentHeight !== dimensions.contentHeight);
if (contentWidthChanged || contentHeightChanged) {
this._onDidContentSizeChange.fire({
contentWidth: dimensions.contentWidth,
contentHeight: dimensions.contentHeight,
contentWidthChanged: contentWidthChanged,
contentHeightChanged: contentHeightChanged
});
this._onDidContentSizeChange.fire(new ContentSizeChangedEvent(
oldDimensions.contentWidth, oldDimensions.contentHeight,
dimensions.contentWidth, dimensions.contentHeight
));
}
}
@@ -153,7 +151,7 @@ export class ViewLayout extends Disposable implements IViewLayout {
private readonly _scrollable: EditorScrollable;
public readonly onDidScroll: Event<ScrollEvent>;
public readonly onDidContentSizeChange: Event<IContentSizeChangedEvent>;
public readonly onDidContentSizeChange: Event<ContentSizeChangedEvent>;
constructor(configuration: IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) {
super();
@@ -324,7 +322,7 @@ export class ViewLayout extends Disposable implements IViewLayout {
}
}
public onMaxLineWidthChanged(maxLineWidth: number): void {
public setMaxLineWidth(maxLineWidth: number): void {
const scrollDimensions = this._scrollable.getScrollDimensions();
// const newScrollWidth = ;
this._scrollable.setScrollDimensions(new EditorScrollDimensions(
@@ -353,8 +351,12 @@ export class ViewLayout extends Disposable implements IViewLayout {
}
// ---- IVerticalLayoutProvider
public changeWhitespace<T>(callback: (accessor: IWhitespaceChangeAccessor) => T): T {
return this._linesLayout.changeWhitespace(callback);
public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean {
const hadAChange = this._linesLayout.changeWhitespace(callback);
if (hadAChange) {
this.onHeightMaybeChanged();
}
return hadAChange;
}
public getVerticalOffsetForLineNumber(lineNumber: number): number {
return this._linesLayout.getVerticalOffsetForLineNumber(lineNumber);
@@ -424,12 +426,12 @@ export class ViewLayout extends Disposable implements IViewLayout {
return this._scrollable.validateScrollPosition(scrollPosition);
}
public setScrollPositionNow(position: INewScrollPosition): void {
this._scrollable.setScrollPositionNow(position);
}
public setScrollPositionSmooth(position: INewScrollPosition): void {
this._scrollable.setScrollPositionSmooth(position);
public setScrollPosition(position: INewScrollPosition, type: ScrollType): void {
if (type === ScrollType.Immediate) {
this._scrollable.setScrollPositionNow(position);
} else {
this._scrollable.setScrollPositionSmooth(position);
}
}
public deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void {

View File

@@ -934,7 +934,9 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
break;
case CharCode.UTF8_BOM:
case CharCode.LINE_SEPARATOR_2028:
case CharCode.LINE_SEPARATOR:
case CharCode.PARAGRAPH_SEPARATOR:
case CharCode.NEXT_LINE:
sb.write1(0xFFFD);
break;

View File

@@ -36,9 +36,7 @@ export class ViewEventHandler extends Disposable {
public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {
return false;
}
public onContentSizeChanged(e: viewEvents.ViewContentSizeChangedEvent): boolean {
return false;
}
public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean {
return false;
}
@@ -102,12 +100,6 @@ export class ViewEventHandler extends Disposable {
}
break;
case viewEvents.ViewEventType.ViewContentSizeChanged:
if (this.onContentSizeChanged(e)) {
shouldRender = true;
}
break;
case viewEvents.ViewEventType.ViewCursorStateChanged:
if (this.onCursorStateChanged(e)) {
shouldRender = true;

View File

@@ -3,18 +3,20 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { IScrollPosition, Scrollable } from 'vs/base/common/scrollable';
import * as strings from 'vs/base/common/strings';
import { IViewLineTokens } from 'vs/editor/common/core/lineTokens';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { INewScrollPosition } from 'vs/editor/common/editorCommon';
import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions } from 'vs/editor/common/model';
import { IViewEventListener } from 'vs/editor/common/view/viewEvents';
import { INewScrollPosition, ScrollType } from 'vs/editor/common/editorCommon';
import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecorationOptions, TextModelResolvedOptions, ITextModel } from 'vs/editor/common/model';
import { VerticalRevealType } from 'vs/editor/common/view/viewEvents';
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
import { EditorTheme } from 'vs/editor/common/view/viewContext';
import { ICursorSimpleModel, PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
export interface IViewWhitespaceViewportData {
readonly id: string;
@@ -43,8 +45,6 @@ export interface IViewLayout {
getScrollable(): Scrollable;
onMaxLineWidthChanged(width: number): void;
getScrollWidth(): number;
getScrollHeight(): number;
@@ -55,9 +55,6 @@ export interface IViewLayout {
getFutureViewport(): Viewport;
validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition;
setScrollPositionNow(position: INewScrollPosition): void;
setScrollPositionSmooth(position: INewScrollPosition): void;
deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void;
getLinesViewportData(): IPartialViewLinesViewportData;
getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData;
@@ -68,18 +65,10 @@ export interface IViewLayout {
getVerticalOffsetForLineNumber(lineNumber: number): number;
getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null;
// --------------- Begin vertical whitespace management
changeWhitespace<T>(callback: (accessor: IWhitespaceChangeAccessor) => T): T;
/**
* Get the layout information for whitespaces currently in the viewport
*/
getWhitespaceViewportData(): IViewWhitespaceViewportData[];
// TODO@Alex whitespace management should work via a change accessor sort of thing
onHeightMaybeChanged(): void;
// --------------- End vertical whitespace management
}
export interface ICoordinatesConverter {
@@ -95,20 +84,26 @@ export interface ICoordinatesConverter {
modelPositionIsVisible(modelPosition: Position): boolean;
}
export interface IViewModel {
export interface IViewModel extends ICursorSimpleModel {
addEventListener(listener: IViewEventListener): IDisposable;
readonly model: ITextModel;
readonly coordinatesConverter: ICoordinatesConverter;
readonly viewLayout: IViewLayout;
readonly cursorConfig: CursorConfiguration;
addViewEventHandler(eventHandler: ViewEventHandler): void;
removeViewEventHandler(eventHandler: ViewEventHandler): void;
/**
* Gives a hint that a lot of requests are about to come in for these line numbers.
*/
setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void;
tokenizeViewport(): void;
setHasFocus(hasFocus: boolean): void;
onDidColorThemeChange(): void;
getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[];
getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData;
@@ -117,7 +112,7 @@ export interface IViewModel {
getCompletelyVisibleViewRange(): Range;
getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range;
getOptions(): TextModelResolvedOptions;
getTextModelOptions(): TextModelResolvedOptions;
getLineCount(): number;
getLineContent(lineNumber: number): string;
getLineLength(lineNumber: number): number;
@@ -140,6 +135,38 @@ export interface IViewModel {
getEOL(): string;
getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[];
getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null;
//#region model
pushStackElement(): void;
//#endregion
//#region cursor
getPrimaryCursorState(): CursorState;
getLastAddedCursorIndex(): number;
getCursorStates(): CursorState[];
setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void;
getCursorColumnSelectData(): IColumnSelectData;
setCursorColumnSelectData(columnSelectData: IColumnSelectData): void;
getPrevEditOperationType(): EditOperationType;
setPrevEditOperationType(type: EditOperationType): void;
revealPrimaryCursor(source: string | null | undefined, revealHorizontal: boolean): void;
revealTopMostCursor(source: string | null | undefined): void;
revealBottomMostCursor(source: string | null | undefined): void;
revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: VerticalRevealType, scrollType: ScrollType): void;
//#endregion
//#region viewLayout
getVerticalOffsetForLineNumber(viewLineNumber: number): number;
getScrollTop(): number;
setScrollTop(newScrollTop: number, scrollType: ScrollType): void;
setScrollPosition(position: INewScrollPosition, type: ScrollType): void;
deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void;
changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void;
setMaxLineWidth(maxLineWidth: number): void;
//#endregion
}
export class MinimapLinesRenderingData {

View File

@@ -0,0 +1,393 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
import { ViewEvent } from 'vs/editor/common/view/viewEvents';
import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
import { Emitter } from 'vs/base/common/event';
import { Selection } from 'vs/editor/common/core/selection';
import { Disposable } from 'vs/base/common/lifecycle';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
export class ViewModelEventDispatcher extends Disposable {
private readonly _onEvent = this._register(new Emitter<OutgoingViewModelEvent>());
public readonly onEvent = this._onEvent.event;
private readonly _eventHandlers: ViewEventHandler[];
private _viewEventQueue: ViewEvent[] | null;
private _isConsumingViewEventQueue: boolean;
private _collector: ViewModelEventsCollector | null;
private _collectorCnt: number;
private _outgoingEvents: OutgoingViewModelEvent[];
constructor() {
super();
this._eventHandlers = [];
this._viewEventQueue = null;
this._isConsumingViewEventQueue = false;
this._collector = null;
this._collectorCnt = 0;
this._outgoingEvents = [];
}
public emitOutgoingEvent(e: OutgoingViewModelEvent): void {
this._addOutgoingEvent(e);
this._emitOugoingEvents();
}
private _addOutgoingEvent(e: OutgoingViewModelEvent): void {
for (let i = 0, len = this._outgoingEvents.length; i < len; i++) {
if (this._outgoingEvents[i].kind === e.kind) {
this._outgoingEvents[i] = this._outgoingEvents[i].merge(e);
return;
}
}
// not merged
this._outgoingEvents.push(e);
}
private _emitOugoingEvents(): void {
while (this._outgoingEvents.length > 0) {
if (this._collector || this._isConsumingViewEventQueue) {
// right now collecting or emitting view events, so let's postpone emitting
return;
}
const event = this._outgoingEvents.shift()!;
if (event.isNoOp()) {
continue;
}
this._onEvent.fire(event);
}
}
public addViewEventHandler(eventHandler: ViewEventHandler): void {
for (let i = 0, len = this._eventHandlers.length; i < len; i++) {
if (this._eventHandlers[i] === eventHandler) {
console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler);
}
}
this._eventHandlers.push(eventHandler);
}
public removeViewEventHandler(eventHandler: ViewEventHandler): void {
for (let i = 0; i < this._eventHandlers.length; i++) {
if (this._eventHandlers[i] === eventHandler) {
this._eventHandlers.splice(i, 1);
break;
}
}
}
public beginEmitViewEvents(): ViewModelEventsCollector {
this._collectorCnt++;
if (this._collectorCnt === 1) {
this._collector = new ViewModelEventsCollector();
}
return this._collector!;
}
public endEmitViewEvents(): void {
this._collectorCnt--;
if (this._collectorCnt === 0) {
const outgoingEvents = this._collector!.outgoingEvents;
const viewEvents = this._collector!.viewEvents;
this._collector = null;
for (const outgoingEvent of outgoingEvents) {
this._addOutgoingEvent(outgoingEvent);
}
if (viewEvents.length > 0) {
this._emitMany(viewEvents);
}
}
this._emitOugoingEvents();
}
public emitSingleViewEvent(event: ViewEvent): void {
try {
const eventsCollector = this.beginEmitViewEvents();
eventsCollector.emitViewEvent(event);
} finally {
this.endEmitViewEvents();
}
}
private _emitMany(events: ViewEvent[]): void {
if (this._viewEventQueue) {
this._viewEventQueue = this._viewEventQueue.concat(events);
} else {
this._viewEventQueue = events;
}
if (!this._isConsumingViewEventQueue) {
this._consumeViewEventQueue();
}
}
private _consumeViewEventQueue(): void {
try {
this._isConsumingViewEventQueue = true;
this._doConsumeQueue();
} finally {
this._isConsumingViewEventQueue = false;
}
}
private _doConsumeQueue(): void {
while (this._viewEventQueue) {
// Empty event queue, as events might come in while sending these off
const events = this._viewEventQueue;
this._viewEventQueue = null;
// Use a clone of the event handlers list, as they might remove themselves
const eventHandlers = this._eventHandlers.slice(0);
for (const eventHandler of eventHandlers) {
eventHandler.handleEvents(events);
}
}
}
}
export class ViewModelEventsCollector {
public readonly viewEvents: ViewEvent[];
public readonly outgoingEvents: OutgoingViewModelEvent[];
constructor() {
this.viewEvents = [];
this.outgoingEvents = [];
}
public emitViewEvent(event: ViewEvent) {
this.viewEvents.push(event);
}
public emitOutgoingEvent(e: OutgoingViewModelEvent): void {
this.outgoingEvents.push(e);
}
}
export const enum OutgoingViewModelEventKind {
ContentSizeChanged,
FocusChanged,
ScrollChanged,
ViewZonesChanged,
ReadOnlyEditAttempt,
CursorStateChanged,
}
export class ContentSizeChangedEvent implements IContentSizeChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ContentSizeChanged;
private readonly _oldContentWidth: number;
private readonly _oldContentHeight: number;
readonly contentWidth: number;
readonly contentHeight: number;
readonly contentWidthChanged: boolean;
readonly contentHeightChanged: boolean;
constructor(oldContentWidth: number, oldContentHeight: number, contentWidth: number, contentHeight: number) {
this._oldContentWidth = oldContentWidth;
this._oldContentHeight = oldContentHeight;
this.contentWidth = contentWidth;
this.contentHeight = contentHeight;
this.contentWidthChanged = (this._oldContentWidth !== this.contentWidth);
this.contentHeightChanged = (this._oldContentHeight !== this.contentHeight);
}
public isNoOp(): boolean {
return (!this.contentWidthChanged && !this.contentHeightChanged);
}
public merge(other: OutgoingViewModelEvent): ContentSizeChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.ContentSizeChanged) {
return this;
}
return new ContentSizeChangedEvent(this._oldContentWidth, this._oldContentHeight, other.contentWidth, other.contentHeight);
}
}
export class FocusChangedEvent {
public readonly kind = OutgoingViewModelEventKind.FocusChanged;
readonly oldHasFocus: boolean;
readonly hasFocus: boolean;
constructor(oldHasFocus: boolean, hasFocus: boolean) {
this.oldHasFocus = oldHasFocus;
this.hasFocus = hasFocus;
}
public isNoOp(): boolean {
return (this.oldHasFocus === this.hasFocus);
}
public merge(other: OutgoingViewModelEvent): FocusChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.FocusChanged) {
return this;
}
return new FocusChangedEvent(this.oldHasFocus, other.hasFocus);
}
}
export class ScrollChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ScrollChanged;
private readonly _oldScrollWidth: number;
private readonly _oldScrollLeft: number;
private readonly _oldScrollHeight: number;
private readonly _oldScrollTop: number;
public readonly scrollWidth: number;
public readonly scrollLeft: number;
public readonly scrollHeight: number;
public readonly scrollTop: number;
public readonly scrollWidthChanged: boolean;
public readonly scrollLeftChanged: boolean;
public readonly scrollHeightChanged: boolean;
public readonly scrollTopChanged: boolean;
constructor(
oldScrollWidth: number, oldScrollLeft: number, oldScrollHeight: number, oldScrollTop: number,
scrollWidth: number, scrollLeft: number, scrollHeight: number, scrollTop: number,
) {
this._oldScrollWidth = oldScrollWidth;
this._oldScrollLeft = oldScrollLeft;
this._oldScrollHeight = oldScrollHeight;
this._oldScrollTop = oldScrollTop;
this.scrollWidth = scrollWidth;
this.scrollLeft = scrollLeft;
this.scrollHeight = scrollHeight;
this.scrollTop = scrollTop;
this.scrollWidthChanged = (this._oldScrollWidth !== this.scrollWidth);
this.scrollLeftChanged = (this._oldScrollLeft !== this.scrollLeft);
this.scrollHeightChanged = (this._oldScrollHeight !== this.scrollHeight);
this.scrollTopChanged = (this._oldScrollTop !== this.scrollTop);
}
public isNoOp(): boolean {
return (!this.scrollWidthChanged && !this.scrollLeftChanged && !this.scrollHeightChanged && !this.scrollTopChanged);
}
public merge(other: OutgoingViewModelEvent): ScrollChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.ScrollChanged) {
return this;
}
return new ScrollChangedEvent(
this._oldScrollWidth, this._oldScrollLeft, this._oldScrollHeight, this._oldScrollTop,
other.scrollWidth, other.scrollLeft, other.scrollHeight, other.scrollTop
);
}
}
export class ViewZonesChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ViewZonesChanged;
constructor() {
}
public isNoOp(): boolean {
return false;
}
public merge(other: OutgoingViewModelEvent): ViewZonesChangedEvent {
return this;
}
}
export class CursorStateChangedEvent {
public readonly kind = OutgoingViewModelEventKind.CursorStateChanged;
public readonly oldSelections: Selection[] | null;
public readonly selections: Selection[];
public readonly oldModelVersionId: number;
public readonly modelVersionId: number;
public readonly source: string;
public readonly reason: CursorChangeReason;
public readonly reachedMaxCursorCount: boolean;
constructor(oldSelections: Selection[] | null, selections: Selection[], oldModelVersionId: number, modelVersionId: number, source: string, reason: CursorChangeReason, reachedMaxCursorCount: boolean) {
this.oldSelections = oldSelections;
this.selections = selections;
this.oldModelVersionId = oldModelVersionId;
this.modelVersionId = modelVersionId;
this.source = source;
this.reason = reason;
this.reachedMaxCursorCount = reachedMaxCursorCount;
}
private static _selectionsAreEqual(a: Selection[] | null, b: Selection[] | null): boolean {
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
const aLen = a.length;
const bLen = b.length;
if (aLen !== bLen) {
return false;
}
for (let i = 0; i < aLen; i++) {
if (!a[i].equalsSelection(b[i])) {
return false;
}
}
return true;
}
public isNoOp(): boolean {
return (
CursorStateChangedEvent._selectionsAreEqual(this.oldSelections, this.selections)
&& this.oldModelVersionId === this.modelVersionId
);
}
public merge(other: OutgoingViewModelEvent): CursorStateChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.CursorStateChanged) {
return this;
}
return new CursorStateChangedEvent(
this.oldSelections, other.selections, this.oldModelVersionId, other.modelVersionId, other.source, other.reason, this.reachedMaxCursorCount || other.reachedMaxCursorCount
);
}
}
export class ReadOnlyEditAttemptEvent {
public readonly kind = OutgoingViewModelEventKind.ReadOnlyEditAttempt;
constructor() {
}
public isNoOp(): boolean {
return false;
}
public merge(other: OutgoingViewModelEvent): ReadOnlyEditAttemptEvent {
return this;
}
}
export type OutgoingViewModelEvent = (
ContentSizeChangedEvent
| FocusChangedEvent
| ScrollChangedEvent
| ViewZonesChangedEvent
| ReadOnlyEditAttemptEvent
| CursorStateChangedEvent
);

View File

@@ -4,13 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { Color } from 'vs/base/common/color';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption, filterValidationDecorations } from 'vs/editor/common/config/editorOptions';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { IRange, Range } from 'vs/editor/common/core/range';
import { IConfiguration, IViewState } from 'vs/editor/common/editorCommon';
import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions } from 'vs/editor/common/model';
import { IConfiguration, IViewState, ScrollType, ICursorState, ICommand, INewScrollPosition } from 'vs/editor/common/editorCommon';
import { EndOfLinePreference, IActiveIndentGuideInfo, ITextModel, TrackedRangeStickiness, TextModelResolvedOptions, IIdentifiedSingleEditOperation, ICursorStateComputer } from 'vs/editor/common/model';
import { ModelDecorationOverviewRulerOptions, ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { ColorId, LanguageId, TokenizationRegistry } from 'vs/editor/common/modes';
@@ -24,24 +26,34 @@ import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecora
import { RunOnceScheduler } from 'vs/base/common/async';
import * as platform from 'vs/base/common/platform';
import { EditorTheme } from 'vs/editor/common/view/viewContext';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { PartialCursorState, CursorState, IColumnSelectData, EditOperationType, CursorConfiguration } from 'vs/editor/common/controller/cursorCommon';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout';
import { ViewModelEventDispatcher, OutgoingViewModelEvent, FocusChangedEvent, ScrollChangedEvent, ViewZonesChangedEvent, ViewModelEventsCollector, ReadOnlyEditAttemptEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
const USE_IDENTITY_LINES_COLLECTION = true;
export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel {
export class ViewModel extends Disposable implements IViewModel {
private readonly editorId: number;
private readonly configuration: IConfiguration;
private readonly model: ITextModel;
private readonly _editorId: number;
private readonly _configuration: IConfiguration;
public readonly model: ITextModel;
private readonly _eventDispatcher: ViewModelEventDispatcher;
public readonly onEvent: Event<OutgoingViewModelEvent>;
public cursorConfig: CursorConfiguration;
private readonly _tokenizeViewportSoon: RunOnceScheduler;
private readonly _updateConfigurationViewLineCount: RunOnceScheduler;
private hasFocus: boolean;
private viewportStartLine: number;
private viewportStartLineTrackedRange: string | null;
private viewportStartLineDelta: number;
private readonly lines: IViewModelLinesCollection;
private _hasFocus: boolean;
private _viewportStartLine: number;
private _viewportStartLineTrackedRange: string | null;
private _viewportStartLineDelta: number;
private readonly _lines: IViewModelLinesCollection;
public readonly coordinatesConverter: ICoordinatesConverter;
public readonly viewLayout: ViewLayout;
private readonly decorations: ViewModelDecorations;
private readonly _cursor: Cursor;
private readonly _decorations: ViewModelDecorations;
constructor(
editorId: number,
@@ -53,28 +65,31 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
) {
super();
this.editorId = editorId;
this.configuration = configuration;
this._editorId = editorId;
this._configuration = configuration;
this.model = model;
this._eventDispatcher = new ViewModelEventDispatcher();
this.onEvent = this._eventDispatcher.onEvent;
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
this._tokenizeViewportSoon = this._register(new RunOnceScheduler(() => this.tokenizeViewport(), 50));
this._updateConfigurationViewLineCount = this._register(new RunOnceScheduler(() => this._updateConfigurationViewLineCountNow(), 0));
this.hasFocus = false;
this.viewportStartLine = -1;
this.viewportStartLineTrackedRange = null;
this.viewportStartLineDelta = 0;
this._hasFocus = false;
this._viewportStartLine = -1;
this._viewportStartLineTrackedRange = null;
this._viewportStartLineDelta = 0;
if (USE_IDENTITY_LINES_COLLECTION && this.model.isTooLargeForTokenization()) {
this.lines = new IdentityLinesCollection(this.model);
this._lines = new IdentityLinesCollection(this.model);
} else {
const options = this.configuration.options;
const options = this._configuration.options;
const fontInfo = options.get(EditorOption.fontInfo);
const wrappingStrategy = options.get(EditorOption.wrappingStrategy);
const wrappingInfo = options.get(EditorOption.wrappingInfo);
const wrappingIndent = options.get(EditorOption.wrappingIndent);
this.lines = new SplitLinesCollection(
this._lines = new SplitLinesCollection(
this.model,
domLineBreaksComputerFactory,
monospaceLineBreaksComputerFactory,
@@ -86,51 +101,42 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
);
}
this.coordinatesConverter = this.lines.createCoordinatesConverter();
this.coordinatesConverter = this._lines.createCoordinatesConverter();
this.viewLayout = this._register(new ViewLayout(this.configuration, this.getLineCount(), scheduleAtNextAnimationFrame));
this._cursor = this._register(new Cursor(model, this, this.coordinatesConverter, this.cursorConfig));
this.viewLayout = this._register(new ViewLayout(this._configuration, this.getLineCount(), scheduleAtNextAnimationFrame));
this._register(this.viewLayout.onDidScroll((e) => {
if (e.scrollTopChanged) {
this._tokenizeViewportSoon.schedule();
}
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewScrollChangedEvent(e));
} finally {
this._endEmit();
}
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewScrollChangedEvent(e));
this._eventDispatcher.emitOutgoingEvent(new ScrollChangedEvent(
e.oldScrollWidth, e.oldScrollLeft, e.oldScrollHeight, e.oldScrollTop,
e.scrollWidth, e.scrollLeft, e.scrollHeight, e.scrollTop
));
}));
this._register(this.viewLayout.onDidContentSizeChange((e) => {
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewContentSizeChangedEvent(e));
} finally {
this._endEmit();
}
this._eventDispatcher.emitOutgoingEvent(e);
}));
this.decorations = new ViewModelDecorations(this.editorId, this.model, this.configuration, this.lines, this.coordinatesConverter);
this._decorations = new ViewModelDecorations(this._editorId, this.model, this._configuration, this._lines, this.coordinatesConverter);
this._registerModelEvents();
this._register(this.configuration.onDidChange((e) => {
this._register(this._configuration.onDidChangeFast((e) => {
try {
const eventsCollector = this._beginEmit();
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
this._onConfigurationChanged(eventsCollector, e);
} finally {
this._endEmit();
this._eventDispatcher.endEmitViewEvents();
}
}));
this._register(MinimapTokensColorTracker.getInstance().onDidChange(() => {
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewTokensColorsChangedEvent());
} finally {
this._endEmit();
}
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent());
}));
this._updateConfigurationViewLineCountNow();
@@ -140,14 +146,23 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
// First remove listeners, as disposing the lines might end up sending
// model decoration changed events ... and we no longer care about them ...
super.dispose();
this.decorations.dispose();
this.lines.dispose();
this._decorations.dispose();
this._lines.dispose();
this.invalidateMinimapColorCache();
this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
this._eventDispatcher.dispose();
}
public addViewEventHandler(eventHandler: ViewEventHandler): void {
this._eventDispatcher.addViewEventHandler(eventHandler);
}
public removeViewEventHandler(eventHandler: ViewEventHandler): void {
this._eventDispatcher.removeViewEventHandler(eventHandler);
}
private _updateConfigurationViewLineCountNow(): void {
this.configuration.setViewLineCount(this.lines.getViewLineCount());
this._configuration.setViewLineCount(this._lines.getViewLineCount());
}
public tokenizeViewport(): void {
@@ -158,30 +173,38 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
public setHasFocus(hasFocus: boolean): void {
this.hasFocus = hasFocus;
this._hasFocus = hasFocus;
this._cursor.setHasFocus(hasFocus);
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewFocusChangedEvent(hasFocus));
this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus));
}
private _onConfigurationChanged(eventsCollector: viewEvents.ViewEventsCollector, e: ConfigurationChangedEvent): void {
public onDidColorThemeChange(): void {
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent());
}
private _onConfigurationChanged(eventsCollector: ViewModelEventsCollector, e: ConfigurationChangedEvent): void {
// We might need to restore the current centered view range, so save it (if available)
let previousViewportStartModelPosition: Position | null = null;
if (this.viewportStartLine !== -1) {
let previousViewportStartViewPosition = new Position(this.viewportStartLine, this.getLineMinColumn(this.viewportStartLine));
if (this._viewportStartLine !== -1) {
let previousViewportStartViewPosition = new Position(this._viewportStartLine, this.getLineMinColumn(this._viewportStartLine));
previousViewportStartModelPosition = this.coordinatesConverter.convertViewPositionToModelPosition(previousViewportStartViewPosition);
}
let restorePreviousViewportStart = false;
const options = this.configuration.options;
const options = this._configuration.options;
const fontInfo = options.get(EditorOption.fontInfo);
const wrappingStrategy = options.get(EditorOption.wrappingStrategy);
const wrappingInfo = options.get(EditorOption.wrappingInfo);
const wrappingIndent = options.get(EditorOption.wrappingIndent);
if (this.lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) {
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
this.decorations.onLineMappingChanged();
if (this._lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) {
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
this._cursor.onLineMappingChanged(eventsCollector);
this._decorations.onLineMappingChanged();
this.viewLayout.onFlushed(this.getLineCount());
if (this.viewLayout.getCurrentScrollTop() !== 0) {
@@ -194,17 +217,22 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
if (e.hasChanged(EditorOption.readOnly)) {
// Must read again all decorations due to readOnly filtering
this.decorations.reset();
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
this._decorations.reset();
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
}
eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e));
eventsCollector.emitViewEvent(new viewEvents.ViewConfigurationChangedEvent(e));
this.viewLayout.onConfigurationChanged(e);
if (restorePreviousViewportStart && previousViewportStartModelPosition) {
const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(previousViewportStartModelPosition);
const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
this.viewLayout.setScrollPositionNow({ scrollTop: viewPositionTop + this.viewportStartLineDelta });
this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, ScrollType.Immediate);
}
if (CursorConfiguration.shouldRecreate(e)) {
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
this._cursor.updateConfiguration(this.cursorConfig);
}
}
@@ -212,7 +240,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
this._register(this.model.onDidChangeRawContentFast((e) => {
try {
const eventsCollector = this._beginEmit();
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
let hadOtherModelChange = false;
let hadModelLineChangeThatChangedLineMapping = false;
@@ -221,7 +249,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
const versionId = e.versionId;
// Do a first pass to compute line mappings, and a second pass to actually interpret them
const lineBreaksComputer = this.lines.createLineBreaksComputer();
const lineBreaksComputer = this._lines.createLineBreaksComputer();
for (const change of changes) {
switch (change.changeType) {
case textModelEvents.RawContentChangedType.LinesInserted: {
@@ -243,17 +271,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
switch (change.changeType) {
case textModelEvents.RawContentChangedType.Flush: {
this.lines.onModelFlushed();
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
this.decorations.reset();
this._lines.onModelFlushed();
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
this._decorations.reset();
this.viewLayout.onFlushed(this.getLineCount());
hadOtherModelChange = true;
break;
}
case textModelEvents.RawContentChangedType.LinesDeleted: {
const linesDeletedEvent = this.lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber);
const linesDeletedEvent = this._lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber);
if (linesDeletedEvent !== null) {
eventsCollector.emit(linesDeletedEvent);
eventsCollector.emitViewEvent(linesDeletedEvent);
this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
}
hadOtherModelChange = true;
@@ -263,9 +291,9 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
const insertedLineBreaks = lineBreaks.slice(lineBreaksOffset, lineBreaksOffset + change.detail.length);
lineBreaksOffset += change.detail.length;
const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks);
const linesInsertedEvent = this._lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks);
if (linesInsertedEvent !== null) {
eventsCollector.emit(linesInsertedEvent);
eventsCollector.emitViewEvent(linesInsertedEvent);
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
}
hadOtherModelChange = true;
@@ -275,17 +303,17 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
const changedLineBreakData = lineBreaks[lineBreaksOffset];
lineBreaksOffset++;
const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData);
const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this._lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData);
hadModelLineChangeThatChangedLineMapping = lineMappingChanged;
if (linesChangedEvent) {
eventsCollector.emit(linesChangedEvent);
eventsCollector.emitViewEvent(linesChangedEvent);
}
if (linesInsertedEvent) {
eventsCollector.emit(linesInsertedEvent);
eventsCollector.emitViewEvent(linesInsertedEvent);
this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
}
if (linesDeletedEvent) {
eventsCollector.emit(linesDeletedEvent);
eventsCollector.emitViewEvent(linesDeletedEvent);
this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
}
break;
@@ -296,32 +324,40 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
}
}
this.lines.acceptVersionId(versionId);
this._lines.acceptVersionId(versionId);
this.viewLayout.onHeightMaybeChanged();
if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
this.decorations.onLineMappingChanged();
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
this._cursor.onLineMappingChanged(eventsCollector);
this._decorations.onLineMappingChanged();
}
} finally {
this._endEmit();
this._eventDispatcher.endEmitViewEvents();
}
// Update the configuration and reset the centered view line
this.viewportStartLine = -1;
this.configuration.setMaxLineNumber(this.model.getLineCount());
this._viewportStartLine = -1;
this._configuration.setMaxLineNumber(this.model.getLineCount());
this._updateConfigurationViewLineCountNow();
// Recover viewport
if (!this.hasFocus && this.model.getAttachedEditorCount() >= 2 && this.viewportStartLineTrackedRange) {
const modelRange = this.model._getTrackedRange(this.viewportStartLineTrackedRange);
if (!this._hasFocus && this.model.getAttachedEditorCount() >= 2 && this._viewportStartLineTrackedRange) {
const modelRange = this.model._getTrackedRange(this._viewportStartLineTrackedRange);
if (modelRange) {
const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelRange.getStartPosition());
const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
this.viewLayout.setScrollPositionNow({ scrollTop: viewPositionTop + this.viewportStartLineDelta });
this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, ScrollType.Immediate);
}
}
try {
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
this._cursor.onModelContentChanged(eventsCollector, e);
} finally {
this._eventDispatcher.endEmitViewEvents();
}
}));
this._register(this.model.onDidChangeTokens((e) => {
@@ -335,12 +371,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
toLineNumber: viewEndLineNumber
};
}
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewTokensChangedEvent(viewRanges));
} finally {
this._endEmit();
}
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensChangedEvent(viewRanges));
if (e.tokenizationSupportChanged) {
this._tokenizeViewportSoon.schedule();
@@ -348,63 +379,65 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}));
this._register(this.model.onDidChangeLanguageConfiguration((e) => {
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewLanguageConfigurationEvent());
} finally {
this._endEmit();
}
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewLanguageConfigurationEvent());
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
this._cursor.updateConfiguration(this.cursorConfig);
}));
this._register(this.model.onDidChangeLanguage((e) => {
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
this._cursor.updateConfiguration(this.cursorConfig);
}));
this._register(this.model.onDidChangeOptions((e) => {
// A tab size change causes a line mapping changed event => all view parts will repaint OK, no further event needed here
if (this.lines.setTabSize(this.model.getOptions().tabSize)) {
this.decorations.onLineMappingChanged();
this.viewLayout.onFlushed(this.getLineCount());
if (this._lines.setTabSize(this.model.getOptions().tabSize)) {
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
this._cursor.onLineMappingChanged(eventsCollector);
this._decorations.onLineMappingChanged();
this.viewLayout.onFlushed(this.getLineCount());
} finally {
this._endEmit();
this._eventDispatcher.endEmitViewEvents();
}
this._updateConfigurationViewLineCount.schedule();
}
this.cursorConfig = new CursorConfiguration(this.model.getLanguageIdentifier(), this.model.getOptions(), this._configuration);
this._cursor.updateConfiguration(this.cursorConfig);
}));
this._register(this.model.onDidChangeDecorations((e) => {
this.decorations.onModelDecorationsChanged();
try {
const eventsCollector = this._beginEmit();
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(e));
} finally {
this._endEmit();
}
this._decorations.onModelDecorationsChanged();
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
}));
}
public setHiddenAreas(ranges: Range[]): void {
try {
const eventsCollector = this._beginEmit();
let lineMappingChanged = this.lines.setHiddenAreas(ranges);
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
let lineMappingChanged = this._lines.setHiddenAreas(ranges);
if (lineMappingChanged) {
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
this.decorations.onLineMappingChanged();
eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
this._cursor.onLineMappingChanged(eventsCollector);
this._decorations.onLineMappingChanged();
this.viewLayout.onFlushed(this.getLineCount());
this.viewLayout.onHeightMaybeChanged();
}
} finally {
this._endEmit();
this._eventDispatcher.endEmitViewEvents();
}
this._updateConfigurationViewLineCount.schedule();
}
public getVisibleRangesPlusViewportAboveBelow(): Range[] {
const layoutInfo = this.configuration.options.get(EditorOption.layoutInfo);
const lineHeight = this.configuration.options.get(EditorOption.lineHeight);
const layoutInfo = this._configuration.options.get(EditorOption.layoutInfo);
const lineHeight = this._configuration.options.get(EditorOption.lineHeight);
const linesAround = Math.max(20, Math.round(layoutInfo.height / lineHeight));
const partialData = this.viewLayout.getLinesViewportData();
const startViewLineNumber = Math.max(1, partialData.completelyVisibleStartLineNumber - linesAround);
@@ -423,7 +456,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
private _toModelVisibleRanges(visibleViewRange: Range): Range[] {
const visibleRange = this.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
const hiddenAreas = this.lines.getHiddenAreas();
const hiddenAreas = this._lines.getHiddenAreas();
if (hiddenAreas.length === 0) {
return [visibleRange];
@@ -528,48 +561,48 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
return this.model.getOptions().tabSize;
}
public getOptions(): TextModelResolvedOptions {
public getTextModelOptions(): TextModelResolvedOptions {
return this.model.getOptions();
}
public getLineCount(): number {
return this.lines.getViewLineCount();
return this._lines.getViewLineCount();
}
/**
* Gives a hint that a lot of requests are about to come in for these line numbers.
*/
public setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void {
this.viewportStartLine = startLineNumber;
this._viewportStartLine = startLineNumber;
let position = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(startLineNumber, this.getLineMinColumn(startLineNumber)));
this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
const viewportStartLineTop = this.viewLayout.getVerticalOffsetForLineNumber(startLineNumber);
const scrollTop = this.viewLayout.getCurrentScrollTop();
this.viewportStartLineDelta = scrollTop - viewportStartLineTop;
this._viewportStartLineDelta = scrollTop - viewportStartLineTop;
}
public getActiveIndentGuide(lineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo {
return this.lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber);
return this._lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber);
}
public getLinesIndentGuides(startLineNumber: number, endLineNumber: number): number[] {
return this.lines.getViewLinesIndentGuides(startLineNumber, endLineNumber);
return this._lines.getViewLinesIndentGuides(startLineNumber, endLineNumber);
}
public getLineContent(lineNumber: number): string {
return this.lines.getViewLineContent(lineNumber);
return this._lines.getViewLineContent(lineNumber);
}
public getLineLength(lineNumber: number): number {
return this.lines.getViewLineLength(lineNumber);
return this._lines.getViewLineLength(lineNumber);
}
public getLineMinColumn(lineNumber: number): number {
return this.lines.getViewLineMinColumn(lineNumber);
return this._lines.getViewLineMinColumn(lineNumber);
}
public getLineMaxColumn(lineNumber: number): number {
return this.lines.getViewLineMaxColumn(lineNumber);
return this._lines.getViewLineMaxColumn(lineNumber);
}
public getLineFirstNonWhitespaceColumn(lineNumber: number): number {
@@ -589,15 +622,15 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
public getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[] {
return this.decorations.getDecorationsViewportData(visibleRange).decorations;
return this._decorations.getDecorationsViewportData(visibleRange).decorations;
}
public getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData {
let mightContainRTL = this.model.mightContainRTL();
let mightContainNonBasicASCII = this.model.mightContainNonBasicASCII();
let tabSize = this.getTabSize();
let lineData = this.lines.getViewLineData(lineNumber);
let allInlineDecorations = this.decorations.getDecorationsViewportData(visibleRange).inlineDecorations;
let lineData = this._lines.getViewLineData(lineNumber);
let allInlineDecorations = this._decorations.getDecorationsViewportData(visibleRange).inlineDecorations;
let inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber];
return new ViewLineRenderingData(
@@ -615,11 +648,11 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
public getViewLineData(lineNumber: number): ViewLineData {
return this.lines.getViewLineData(lineNumber);
return this._lines.getViewLineData(lineNumber);
}
public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData {
let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed);
let result = this._lines.getViewLinesData(startLineNumber, endLineNumber, needed);
return new MinimapLinesRenderingData(
this.getTabSize(),
result
@@ -627,7 +660,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations {
return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme);
return this._lines.getAllOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options), theme);
}
public invalidateOverviewRulerColorCache(): void {
@@ -769,7 +802,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber));
}
const fontInfo = this.configuration.options.get(EditorOption.fontInfo);
const fontInfo = this._configuration.options.get(EditorOption.fontInfo);
const colorMap = this._getColorMap();
const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`;
@@ -827,4 +860,150 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
}
return result;
}
//#region model
public pushStackElement(): void {
this.model.pushStackElement();
}
//#endregion
//#region cursor operations
public getPrimaryCursorState(): CursorState {
return this._cursor.getPrimaryCursorState();
}
public getLastAddedCursorIndex(): number {
return this._cursor.getLastAddedCursorIndex();
}
public getCursorStates(): CursorState[] {
return this._cursor.getCursorStates();
}
public setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void {
this._withViewEventsCollector(eventsCollector => this._cursor.setStates(eventsCollector, source, reason, states));
}
public getCursorColumnSelectData(): IColumnSelectData {
return this._cursor.getCursorColumnSelectData();
}
public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void {
this._cursor.setCursorColumnSelectData(columnSelectData);
}
public getPrevEditOperationType(): EditOperationType {
return this._cursor.getPrevEditOperationType();
}
public setPrevEditOperationType(type: EditOperationType): void {
this._cursor.setPrevEditOperationType(type);
}
public getSelection(): Selection {
return this._cursor.getSelection();
}
public getSelections(): Selection[] {
return this._cursor.getSelections();
}
public getPosition(): Position {
return this._cursor.getPrimaryCursorState().modelState.position;
}
public setSelections(source: string | null | undefined, selections: readonly ISelection[]): void {
this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections));
}
public saveCursorState(): ICursorState[] {
return this._cursor.saveState();
}
public restoreCursorState(states: ICursorState[]): void {
this._withViewEventsCollector(eventsCollector => this._cursor.restoreState(eventsCollector, states));
}
private _executeCursorEdit(callback: (eventsCollector: ViewModelEventsCollector) => void): void {
if (this._cursor.context.cursorConfig.readOnly) {
// we cannot edit when read only...
this._eventDispatcher.emitOutgoingEvent(new ReadOnlyEditAttemptEvent());
return;
}
this._withViewEventsCollector(callback);
}
public executeEdits(source: string | null | undefined, edits: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): void {
this._executeCursorEdit(eventsCollector => this._cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer));
}
public startComposition(): void {
this._cursor.setIsDoingComposition(true);
this._executeCursorEdit(eventsCollector => this._cursor.startComposition(eventsCollector));
}
public endComposition(source?: string | null | undefined): void {
this._cursor.setIsDoingComposition(false);
this._executeCursorEdit(eventsCollector => this._cursor.endComposition(eventsCollector, source));
}
public type(text: string, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source));
}
public replacePreviousChar(text: string, replaceCharCnt: number, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.replacePreviousChar(eventsCollector, text, replaceCharCnt, source));
}
public paste(text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source));
}
public cut(source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.cut(eventsCollector, source));
}
public executeCommand(command: ICommand, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.executeCommand(eventsCollector, command, source));
}
public executeCommands(commands: ICommand[], source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.executeCommands(eventsCollector, commands, source));
}
public revealPrimaryCursor(source: string | null | undefined, revealHorizontal: boolean): void {
this._withViewEventsCollector(eventsCollector => this._cursor.revealPrimary(eventsCollector, source, revealHorizontal, ScrollType.Smooth));
}
public revealTopMostCursor(source: string | null | undefined): void {
const viewPosition = this._cursor.getTopMostViewPosition();
const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth)));
}
public revealBottomMostCursor(source: string | null | undefined): void {
const viewPosition = this._cursor.getBottomMostViewPosition();
const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, viewEvents.VerticalRevealType.Simple, true, ScrollType.Smooth)));
}
public revealRange(source: string | null | undefined, revealHorizontal: boolean, viewRange: Range, verticalType: viewEvents.VerticalRevealType, scrollType: ScrollType): void {
this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, verticalType, revealHorizontal, scrollType)));
}
//#endregion
//#region viewLayout
public getVerticalOffsetForLineNumber(viewLineNumber: number): number {
return this.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber);
}
public getScrollTop(): number {
return this.viewLayout.getCurrentScrollTop();
}
public setScrollTop(newScrollTop: number, scrollType: ScrollType): void {
this.viewLayout.setScrollPosition({ scrollTop: newScrollTop }, scrollType);
}
public setScrollPosition(position: INewScrollPosition, type: ScrollType): void {
this.viewLayout.setScrollPosition(position, type);
}
public deltaScrollNow(deltaScrollLeft: number, deltaScrollTop: number): void {
this.viewLayout.deltaScrollNow(deltaScrollLeft, deltaScrollTop);
}
public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): void {
const hadAChange = this.viewLayout.changeWhitespace(callback);
if (hadAChange) {
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewZonesChangedEvent());
this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent());
}
}
public setMaxLineWidth(maxLineWidth: number): void {
this.viewLayout.setMaxLineWidth(maxLineWidth);
}
//#endregion
private _withViewEventsCollector(callback: (eventsCollector: ViewModelEventsCollector) => void): void {
try {
const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
callback(eventsCollector);
} finally {
this._eventDispatcher.endEmitViewEvents();
}
}
}