123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import { Color } from '../../../base/common/color.js';
- import { Disposable } from '../../../base/common/lifecycle.js';
- import * as strings from '../../../base/common/strings.js';
- import { EDITOR_FONT_DEFAULTS, filterValidationDecorations } from '../config/editorOptions.js';
- import { Position } from '../core/position.js';
- import { Range } from '../core/range.js';
- import * as textModelEvents from '../model/textModelEvents.js';
- import { TokenizationRegistry } from '../modes.js';
- import { tokenizeLineToHTML } from '../modes/textToHtmlTokenizer.js';
- import { MinimapTokensColorTracker } from './minimapTokensColorTracker.js';
- import * as viewEvents from '../view/viewEvents.js';
- import { ViewLayout } from '../viewLayout/viewLayout.js';
- import { ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from './viewModelLines.js';
- import { MinimapLinesRenderingData, ViewLineRenderingData, OverviewRulerDecorationsGroup } from './viewModel.js';
- import { ViewModelDecorations } from './viewModelDecorations.js';
- import { RunOnceScheduler } from '../../../base/common/async.js';
- import * as platform from '../../../base/common/platform.js';
- import { CursorsController } from '../controller/cursor.js';
- import { CursorConfiguration } from '../controller/cursorCommon.js';
- import { ViewModelEventDispatcher, FocusChangedEvent, ScrollChangedEvent, ViewZonesChangedEvent, ReadOnlyEditAttemptEvent } from './viewModelEventDispatcher.js';
- import { PLAINTEXT_MODE_ID } from '../modes/modesRegistry.js';
- import { ArrayQueue } from '../../../base/common/arrays.js';
- const USE_IDENTITY_LINES_COLLECTION = true;
- export class ViewModel extends Disposable {
- constructor(editorId, configuration, model, domLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, scheduleAtNextAnimationFrame) {
- super();
- this._editorId = editorId;
- this._configuration = configuration;
- this.model = model;
- this._eventDispatcher = new ViewModelEventDispatcher();
- this.onEvent = this._eventDispatcher.onEvent;
- this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), 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;
- if (USE_IDENTITY_LINES_COLLECTION && this.model.isTooLargeForTokenization()) {
- this._lines = new ViewModelLinesFromModelAsIs(this.model);
- }
- else {
- const options = this._configuration.options;
- const fontInfo = options.get(43 /* fontInfo */);
- const wrappingStrategy = options.get(124 /* wrappingStrategy */);
- const wrappingInfo = options.get(131 /* wrappingInfo */);
- const wrappingIndent = options.get(123 /* wrappingIndent */);
- this._lines = new ViewModelLinesFromProjectedModel(this._editorId, this.model, domLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, fontInfo, this.model.getOptions().tabSize, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent);
- }
- this.coordinatesConverter = this._lines.createCoordinatesConverter();
- this._cursor = this._register(new CursorsController(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();
- }
- 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) => {
- this._eventDispatcher.emitOutgoingEvent(e);
- }));
- this._decorations = new ViewModelDecorations(this._editorId, this.model, this._configuration, this._lines, this.coordinatesConverter);
- this._registerModelEvents();
- this._register(this._configuration.onDidChangeFast((e) => {
- try {
- const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
- this._onConfigurationChanged(eventsCollector, e);
- }
- finally {
- this._eventDispatcher.endEmitViewEvents();
- }
- }));
- this._register(MinimapTokensColorTracker.getInstance().onDidChange(() => {
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent());
- }));
- this._updateConfigurationViewLineCountNow();
- }
- dispose() {
- // 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.invalidateMinimapColorCache();
- this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, null, 1 /* NeverGrowsWhenTypingAtEdges */);
- this._eventDispatcher.dispose();
- }
- createLineBreaksComputer() {
- return this._lines.createLineBreaksComputer();
- }
- addViewEventHandler(eventHandler) {
- this._eventDispatcher.addViewEventHandler(eventHandler);
- }
- removeViewEventHandler(eventHandler) {
- this._eventDispatcher.removeViewEventHandler(eventHandler);
- }
- _updateConfigurationViewLineCountNow() {
- this._configuration.setViewLineCount(this._lines.getViewLineCount());
- }
- tokenizeViewport() {
- const linesViewportData = this.viewLayout.getLinesViewportData();
- const viewVisibleRange = new Range(linesViewportData.startLineNumber, this.getLineMinColumn(linesViewportData.startLineNumber), linesViewportData.endLineNumber, this.getLineMaxColumn(linesViewportData.endLineNumber));
- const modelVisibleRanges = this._toModelVisibleRanges(viewVisibleRange);
- for (const modelVisibleRange of modelVisibleRanges) {
- this.model.tokenizeViewport(modelVisibleRange.startLineNumber, modelVisibleRange.endLineNumber);
- }
- }
- setHasFocus(hasFocus) {
- this._hasFocus = hasFocus;
- this._cursor.setHasFocus(hasFocus);
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewFocusChangedEvent(hasFocus));
- this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus));
- }
- onCompositionStart() {
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionStartEvent());
- }
- onCompositionEnd() {
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionEndEvent());
- }
- onDidColorThemeChange() {
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent());
- }
- _onConfigurationChanged(eventsCollector, e) {
- // We might need to restore the current centered view range, so save it (if available)
- let previousViewportStartModelPosition = null;
- 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 fontInfo = options.get(43 /* fontInfo */);
- const wrappingStrategy = options.get(124 /* wrappingStrategy */);
- const wrappingInfo = options.get(131 /* wrappingInfo */);
- const wrappingIndent = options.get(123 /* wrappingIndent */);
- 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) {
- // Never change the scroll position from 0 to something else...
- restorePreviousViewportStart = true;
- }
- this._updateConfigurationViewLineCount.schedule();
- }
- if (e.hasChanged(80 /* readOnly */)) {
- // Must read again all decorations due to readOnly filtering
- this._decorations.reset();
- eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
- }
- 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.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, 1 /* Immediate */);
- }
- if (CursorConfiguration.shouldRecreate(e)) {
- this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
- this._cursor.updateConfiguration(this.cursorConfig);
- }
- }
- _registerModelEvents() {
- this._register(this.model.onDidChangeContentOrInjectedText((e) => {
- try {
- const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
- let hadOtherModelChange = false;
- let hadModelLineChangeThatChangedLineMapping = false;
- const changes = e.changes;
- const versionId = (e instanceof textModelEvents.ModelRawContentChangedEvent ? e.versionId : null);
- // Do a first pass to compute line mappings, and a second pass to actually interpret them
- const lineBreaksComputer = this._lines.createLineBreaksComputer();
- for (const change of changes) {
- switch (change.changeType) {
- case 4 /* LinesInserted */: {
- for (let lineIdx = 0; lineIdx < change.detail.length; lineIdx++) {
- const line = change.detail[lineIdx];
- let injectedText = change.injectedTexts[lineIdx];
- if (injectedText) {
- injectedText = injectedText.filter(element => (!element.ownerId || element.ownerId === this._editorId));
- }
- lineBreaksComputer.addRequest(line, injectedText, null);
- }
- break;
- }
- case 2 /* LineChanged */: {
- let injectedText = null;
- if (change.injectedText) {
- injectedText = change.injectedText.filter(element => (!element.ownerId || element.ownerId === this._editorId));
- }
- lineBreaksComputer.addRequest(change.detail, injectedText, null);
- break;
- }
- }
- }
- const lineBreaks = lineBreaksComputer.finalize();
- const lineBreakQueue = new ArrayQueue(lineBreaks);
- for (const change of changes) {
- switch (change.changeType) {
- case 1 /* Flush */: {
- this._lines.onModelFlushed();
- eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
- this._decorations.reset();
- this.viewLayout.onFlushed(this.getLineCount());
- hadOtherModelChange = true;
- break;
- }
- case 3 /* LinesDeleted */: {
- const linesDeletedEvent = this._lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber);
- if (linesDeletedEvent !== null) {
- eventsCollector.emitViewEvent(linesDeletedEvent);
- this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
- }
- hadOtherModelChange = true;
- break;
- }
- case 4 /* LinesInserted */: {
- const insertedLineBreaks = lineBreakQueue.takeCount(change.detail.length);
- const linesInsertedEvent = this._lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks);
- if (linesInsertedEvent !== null) {
- eventsCollector.emitViewEvent(linesInsertedEvent);
- this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
- }
- hadOtherModelChange = true;
- break;
- }
- case 2 /* LineChanged */: {
- const changedLineBreakData = lineBreakQueue.dequeue();
- const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this._lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData);
- hadModelLineChangeThatChangedLineMapping = lineMappingChanged;
- if (linesChangedEvent) {
- eventsCollector.emitViewEvent(linesChangedEvent);
- }
- if (linesInsertedEvent) {
- eventsCollector.emitViewEvent(linesInsertedEvent);
- this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
- }
- if (linesDeletedEvent) {
- eventsCollector.emitViewEvent(linesDeletedEvent);
- this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
- }
- break;
- }
- case 5 /* EOLChanged */: {
- // Nothing to do. The new version will be accepted below
- break;
- }
- }
- }
- if (versionId !== null) {
- this._lines.acceptVersionId(versionId);
- }
- this.viewLayout.onHeightMaybeChanged();
- if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
- eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
- eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
- this._cursor.onLineMappingChanged(eventsCollector);
- this._decorations.onLineMappingChanged();
- }
- }
- finally {
- this._eventDispatcher.endEmitViewEvents();
- }
- // Update the configuration and reset the centered view line
- 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 (modelRange) {
- const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelRange.getStartPosition());
- const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
- this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, 1 /* Immediate */);
- }
- }
- try {
- const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
- this._cursor.onModelContentChanged(eventsCollector, e);
- }
- finally {
- this._eventDispatcher.endEmitViewEvents();
- }
- }));
- this._register(this.model.onDidChangeTokens((e) => {
- let viewRanges = [];
- for (let j = 0, lenJ = e.ranges.length; j < lenJ; j++) {
- const modelRange = e.ranges[j];
- const viewStartLineNumber = this.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.fromLineNumber, 1)).lineNumber;
- const viewEndLineNumber = this.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.toLineNumber, this.model.getLineMaxColumn(modelRange.toLineNumber))).lineNumber;
- viewRanges[j] = {
- fromLineNumber: viewStartLineNumber,
- toLineNumber: viewEndLineNumber
- };
- }
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensChangedEvent(viewRanges));
- if (e.tokenizationSupportChanged) {
- this._tokenizeViewportSoon.schedule();
- }
- }));
- this._register(this.model.onDidChangeLanguageConfiguration((e) => {
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewLanguageConfigurationEvent());
- this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
- this._cursor.updateConfiguration(this.cursorConfig);
- }));
- this._register(this.model.onDidChangeLanguage((e) => {
- this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), 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)) {
- try {
- 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._eventDispatcher.endEmitViewEvents();
- }
- this._updateConfigurationViewLineCount.schedule();
- }
- this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
- this._cursor.updateConfiguration(this.cursorConfig);
- }));
- this._register(this.model.onDidChangeDecorations((e) => {
- this._decorations.onModelDecorationsChanged();
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
- }));
- }
- setHiddenAreas(ranges) {
- let lineMappingChanged = false;
- try {
- const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
- lineMappingChanged = this._lines.setHiddenAreas(ranges);
- if (lineMappingChanged) {
- 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._eventDispatcher.endEmitViewEvents();
- }
- this._updateConfigurationViewLineCount.schedule();
- if (lineMappingChanged) {
- this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent());
- }
- }
- getVisibleRangesPlusViewportAboveBelow() {
- const layoutInfo = this._configuration.options.get(130 /* layoutInfo */);
- const lineHeight = this._configuration.options.get(58 /* lineHeight */);
- const linesAround = Math.max(20, Math.round(layoutInfo.height / lineHeight));
- const partialData = this.viewLayout.getLinesViewportData();
- const startViewLineNumber = Math.max(1, partialData.completelyVisibleStartLineNumber - linesAround);
- const endViewLineNumber = Math.min(this.getLineCount(), partialData.completelyVisibleEndLineNumber + linesAround);
- return this._toModelVisibleRanges(new Range(startViewLineNumber, this.getLineMinColumn(startViewLineNumber), endViewLineNumber, this.getLineMaxColumn(endViewLineNumber)));
- }
- getVisibleRanges() {
- const visibleViewRange = this.getCompletelyVisibleViewRange();
- return this._toModelVisibleRanges(visibleViewRange);
- }
- _toModelVisibleRanges(visibleViewRange) {
- const visibleRange = this.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
- const hiddenAreas = this._lines.getHiddenAreas();
- if (hiddenAreas.length === 0) {
- return [visibleRange];
- }
- let result = [], resultLen = 0;
- let startLineNumber = visibleRange.startLineNumber;
- let startColumn = visibleRange.startColumn;
- let endLineNumber = visibleRange.endLineNumber;
- let endColumn = visibleRange.endColumn;
- for (let i = 0, len = hiddenAreas.length; i < len; i++) {
- const hiddenStartLineNumber = hiddenAreas[i].startLineNumber;
- const hiddenEndLineNumber = hiddenAreas[i].endLineNumber;
- if (hiddenEndLineNumber < startLineNumber) {
- continue;
- }
- if (hiddenStartLineNumber > endLineNumber) {
- continue;
- }
- if (startLineNumber < hiddenStartLineNumber) {
- result[resultLen++] = new Range(startLineNumber, startColumn, hiddenStartLineNumber - 1, this.model.getLineMaxColumn(hiddenStartLineNumber - 1));
- }
- startLineNumber = hiddenEndLineNumber + 1;
- startColumn = 1;
- }
- if (startLineNumber < endLineNumber || (startLineNumber === endLineNumber && startColumn < endColumn)) {
- result[resultLen++] = new Range(startLineNumber, startColumn, endLineNumber, endColumn);
- }
- return result;
- }
- getCompletelyVisibleViewRange() {
- const partialData = this.viewLayout.getLinesViewportData();
- const startViewLineNumber = partialData.completelyVisibleStartLineNumber;
- const endViewLineNumber = partialData.completelyVisibleEndLineNumber;
- return new Range(startViewLineNumber, this.getLineMinColumn(startViewLineNumber), endViewLineNumber, this.getLineMaxColumn(endViewLineNumber));
- }
- getCompletelyVisibleViewRangeAtScrollTop(scrollTop) {
- const partialData = this.viewLayout.getLinesViewportDataAtScrollTop(scrollTop);
- const startViewLineNumber = partialData.completelyVisibleStartLineNumber;
- const endViewLineNumber = partialData.completelyVisibleEndLineNumber;
- return new Range(startViewLineNumber, this.getLineMinColumn(startViewLineNumber), endViewLineNumber, this.getLineMaxColumn(endViewLineNumber));
- }
- saveState() {
- const compatViewState = this.viewLayout.saveState();
- const scrollTop = compatViewState.scrollTop;
- const firstViewLineNumber = this.viewLayout.getLineNumberAtVerticalOffset(scrollTop);
- const firstPosition = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(firstViewLineNumber, this.getLineMinColumn(firstViewLineNumber)));
- const firstPositionDeltaTop = this.viewLayout.getVerticalOffsetForLineNumber(firstViewLineNumber) - scrollTop;
- return {
- scrollLeft: compatViewState.scrollLeft,
- firstPosition: firstPosition,
- firstPositionDeltaTop: firstPositionDeltaTop
- };
- }
- reduceRestoreState(state) {
- if (typeof state.firstPosition === 'undefined') {
- // This is a view state serialized by an older version
- return this._reduceRestoreStateCompatibility(state);
- }
- const modelPosition = this.model.validatePosition(state.firstPosition);
- const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
- const scrollTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber) - state.firstPositionDeltaTop;
- return {
- scrollLeft: state.scrollLeft,
- scrollTop: scrollTop
- };
- }
- _reduceRestoreStateCompatibility(state) {
- return {
- scrollLeft: state.scrollLeft,
- scrollTop: state.scrollTopWithoutViewZones
- };
- }
- getTabSize() {
- return this.model.getOptions().tabSize;
- }
- getTextModelOptions() {
- return this.model.getOptions();
- }
- getLineCount() {
- return this._lines.getViewLineCount();
- }
- /**
- * Gives a hint that a lot of requests are about to come in for these line numbers.
- */
- setViewport(startLineNumber, endLineNumber, centeredLineNumber) {
- 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), 1 /* NeverGrowsWhenTypingAtEdges */);
- const viewportStartLineTop = this.viewLayout.getVerticalOffsetForLineNumber(startLineNumber);
- const scrollTop = this.viewLayout.getCurrentScrollTop();
- this._viewportStartLineDelta = scrollTop - viewportStartLineTop;
- }
- getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber) {
- return this._lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber);
- }
- getLinesIndentGuides(startLineNumber, endLineNumber) {
- return this._lines.getViewLinesIndentGuides(startLineNumber, endLineNumber);
- }
- getBracketGuidesInRangeByLine(startLineNumber, endLineNumber, activePosition, options) {
- return this._lines.getViewLinesBracketGuides(startLineNumber, endLineNumber, activePosition, options);
- }
- getLineContent(lineNumber) {
- return this._lines.getViewLineContent(lineNumber);
- }
- getLineLength(lineNumber) {
- return this._lines.getViewLineLength(lineNumber);
- }
- getLineMinColumn(lineNumber) {
- return this._lines.getViewLineMinColumn(lineNumber);
- }
- getLineMaxColumn(lineNumber) {
- return this._lines.getViewLineMaxColumn(lineNumber);
- }
- getLineFirstNonWhitespaceColumn(lineNumber) {
- const result = strings.firstNonWhitespaceIndex(this.getLineContent(lineNumber));
- if (result === -1) {
- return 0;
- }
- return result + 1;
- }
- getLineLastNonWhitespaceColumn(lineNumber) {
- const result = strings.lastNonWhitespaceIndex(this.getLineContent(lineNumber));
- if (result === -1) {
- return 0;
- }
- return result + 2;
- }
- getDecorationsInViewport(visibleRange) {
- return this._decorations.getDecorationsViewportData(visibleRange).decorations;
- }
- getInjectedTextAt(viewPosition) {
- return this._lines.getInjectedTextAt(viewPosition);
- }
- getViewLineRenderingData(visibleRange, lineNumber) {
- 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 inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber];
- if (lineData.inlineDecorations) {
- inlineDecorations = [
- ...inlineDecorations,
- ...lineData.inlineDecorations.map(d => d.toInlineDecoration(lineNumber))
- ];
- }
- return new ViewLineRenderingData(lineData.minColumn, lineData.maxColumn, lineData.content, lineData.continuesWithWrappedLine, mightContainRTL, mightContainNonBasicASCII, lineData.tokens, inlineDecorations, tabSize, lineData.startVisibleColumn);
- }
- getViewLineData(lineNumber) {
- return this._lines.getViewLineData(lineNumber);
- }
- getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed) {
- let result = this._lines.getViewLinesData(startLineNumber, endLineNumber, needed);
- return new MinimapLinesRenderingData(this.getTabSize(), result);
- }
- getAllOverviewRulerDecorations(theme) {
- const decorations = this.model.getOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options));
- const result = new OverviewRulerDecorations();
- for (const decoration of decorations) {
- const decorationOptions = decoration.options;
- const opts = decorationOptions.overviewRuler;
- if (!opts) {
- continue;
- }
- const lane = opts.position;
- if (lane === 0) {
- continue;
- }
- const color = opts.getColor(theme);
- const viewStartLineNumber = this.coordinatesConverter.getViewLineNumberOfModelPosition(decoration.range.startLineNumber, decoration.range.startColumn);
- const viewEndLineNumber = this.coordinatesConverter.getViewLineNumberOfModelPosition(decoration.range.endLineNumber, decoration.range.endColumn);
- result.accept(color, decorationOptions.zIndex, viewStartLineNumber, viewEndLineNumber, lane);
- }
- return result.asArray;
- }
- invalidateOverviewRulerColorCache() {
- const decorations = this.model.getOverviewRulerDecorations();
- for (const decoration of decorations) {
- const opts = decoration.options.overviewRuler;
- if (opts) {
- opts.invalidateCachedColor();
- }
- }
- }
- invalidateMinimapColorCache() {
- const decorations = this.model.getAllDecorations();
- for (const decoration of decorations) {
- const opts = decoration.options.minimap;
- if (opts) {
- opts.invalidateCachedColor();
- }
- }
- }
- getValueInRange(range, eol) {
- const modelRange = this.coordinatesConverter.convertViewRangeToModelRange(range);
- return this.model.getValueInRange(modelRange, eol);
- }
- getModelLineMaxColumn(modelLineNumber) {
- return this.model.getLineMaxColumn(modelLineNumber);
- }
- validateModelPosition(position) {
- return this.model.validatePosition(position);
- }
- validateModelRange(range) {
- return this.model.validateRange(range);
- }
- deduceModelPositionRelativeToViewPosition(viewAnchorPosition, deltaOffset, lineFeedCnt) {
- const modelAnchor = this.coordinatesConverter.convertViewPositionToModelPosition(viewAnchorPosition);
- if (this.model.getEOL().length === 2) {
- // This model uses CRLF, so the delta must take that into account
- if (deltaOffset < 0) {
- deltaOffset -= lineFeedCnt;
- }
- else {
- deltaOffset += lineFeedCnt;
- }
- }
- const modelAnchorOffset = this.model.getOffsetAt(modelAnchor);
- const resultOffset = modelAnchorOffset + deltaOffset;
- return this.model.getPositionAt(resultOffset);
- }
- getEOL() {
- return this.model.getEOL();
- }
- getPlainTextToCopy(modelRanges, emptySelectionClipboard, forceCRLF) {
- const newLineCharacter = forceCRLF ? '\r\n' : this.model.getEOL();
- modelRanges = modelRanges.slice(0);
- modelRanges.sort(Range.compareRangesUsingStarts);
- let hasEmptyRange = false;
- let hasNonEmptyRange = false;
- for (const range of modelRanges) {
- if (range.isEmpty()) {
- hasEmptyRange = true;
- }
- else {
- hasNonEmptyRange = true;
- }
- }
- if (!hasNonEmptyRange) {
- // all ranges are empty
- if (!emptySelectionClipboard) {
- return '';
- }
- const modelLineNumbers = modelRanges.map((r) => r.startLineNumber);
- let result = '';
- for (let i = 0; i < modelLineNumbers.length; i++) {
- if (i > 0 && modelLineNumbers[i - 1] === modelLineNumbers[i]) {
- continue;
- }
- result += this.model.getLineContent(modelLineNumbers[i]) + newLineCharacter;
- }
- return result;
- }
- if (hasEmptyRange && emptySelectionClipboard) {
- // mixed empty selections and non-empty selections
- let result = [];
- let prevModelLineNumber = 0;
- for (const modelRange of modelRanges) {
- const modelLineNumber = modelRange.startLineNumber;
- if (modelRange.isEmpty()) {
- if (modelLineNumber !== prevModelLineNumber) {
- result.push(this.model.getLineContent(modelLineNumber));
- }
- }
- else {
- result.push(this.model.getValueInRange(modelRange, forceCRLF ? 2 /* CRLF */ : 0 /* TextDefined */));
- }
- prevModelLineNumber = modelLineNumber;
- }
- return result.length === 1 ? result[0] : result;
- }
- let result = [];
- for (const modelRange of modelRanges) {
- if (!modelRange.isEmpty()) {
- result.push(this.model.getValueInRange(modelRange, forceCRLF ? 2 /* CRLF */ : 0 /* TextDefined */));
- }
- }
- return result.length === 1 ? result[0] : result;
- }
- getRichTextToCopy(modelRanges, emptySelectionClipboard) {
- const languageId = this.model.getLanguageId();
- if (languageId === PLAINTEXT_MODE_ID) {
- return null;
- }
- if (modelRanges.length !== 1) {
- // no multiple selection support at this time
- return null;
- }
- let range = modelRanges[0];
- if (range.isEmpty()) {
- if (!emptySelectionClipboard) {
- // nothing to copy
- return null;
- }
- const lineNumber = range.startLineNumber;
- range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber));
- }
- const fontInfo = this._configuration.options.get(43 /* fontInfo */);
- const colorMap = this._getColorMap();
- const hasBadChars = (/[:;\\\/<>]/.test(fontInfo.fontFamily));
- const useDefaultFontFamily = (hasBadChars || fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily);
- let fontFamily;
- if (useDefaultFontFamily) {
- fontFamily = EDITOR_FONT_DEFAULTS.fontFamily;
- }
- else {
- fontFamily = fontInfo.fontFamily;
- fontFamily = fontFamily.replace(/"/g, '\'');
- const hasQuotesOrIsList = /[,']/.test(fontFamily);
- if (!hasQuotesOrIsList) {
- const needsQuotes = /[+ ]/.test(fontFamily);
- if (needsQuotes) {
- fontFamily = `'${fontFamily}'`;
- }
- }
- fontFamily = `${fontFamily}, ${EDITOR_FONT_DEFAULTS.fontFamily}`;
- }
- return {
- mode: languageId,
- html: (`<div style="`
- + `color: ${colorMap[1 /* DefaultForeground */]};`
- + `background-color: ${colorMap[2 /* DefaultBackground */]};`
- + `font-family: ${fontFamily};`
- + `font-weight: ${fontInfo.fontWeight};`
- + `font-size: ${fontInfo.fontSize}px;`
- + `line-height: ${fontInfo.lineHeight}px;`
- + `white-space: pre;`
- + `">`
- + this._getHTMLToCopy(range, colorMap)
- + '</div>')
- };
- }
- _getHTMLToCopy(modelRange, colorMap) {
- const startLineNumber = modelRange.startLineNumber;
- const startColumn = modelRange.startColumn;
- const endLineNumber = modelRange.endLineNumber;
- const endColumn = modelRange.endColumn;
- const tabSize = this.getTabSize();
- let result = '';
- for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
- const lineTokens = this.model.getLineTokens(lineNumber);
- const lineContent = lineTokens.getLineContent();
- const startOffset = (lineNumber === startLineNumber ? startColumn - 1 : 0);
- const endOffset = (lineNumber === endLineNumber ? endColumn - 1 : lineContent.length);
- if (lineContent === '') {
- result += '<br>';
- }
- else {
- result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize, platform.isWindows);
- }
- }
- return result;
- }
- _getColorMap() {
- let colorMap = TokenizationRegistry.getColorMap();
- let result = ['#000000'];
- if (colorMap) {
- for (let i = 1, len = colorMap.length; i < len; i++) {
- result[i] = Color.Format.CSS.formatHex(colorMap[i]);
- }
- }
- return result;
- }
- //#region model
- pushStackElement() {
- this.model.pushStackElement();
- }
- //#endregion
- //#region cursor operations
- getPrimaryCursorState() {
- return this._cursor.getPrimaryCursorState();
- }
- getLastAddedCursorIndex() {
- return this._cursor.getLastAddedCursorIndex();
- }
- getCursorStates() {
- return this._cursor.getCursorStates();
- }
- setCursorStates(source, reason, states) {
- this._withViewEventsCollector(eventsCollector => this._cursor.setStates(eventsCollector, source, reason, states));
- }
- getCursorColumnSelectData() {
- return this._cursor.getCursorColumnSelectData();
- }
- getCursorAutoClosedCharacters() {
- return this._cursor.getAutoClosedCharacters();
- }
- setCursorColumnSelectData(columnSelectData) {
- this._cursor.setCursorColumnSelectData(columnSelectData);
- }
- getPrevEditOperationType() {
- return this._cursor.getPrevEditOperationType();
- }
- setPrevEditOperationType(type) {
- this._cursor.setPrevEditOperationType(type);
- }
- getSelection() {
- return this._cursor.getSelection();
- }
- getSelections() {
- return this._cursor.getSelections();
- }
- getPosition() {
- return this._cursor.getPrimaryCursorState().modelState.position;
- }
- setSelections(source, selections, reason = 0 /* NotSet */) {
- this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections, reason));
- }
- saveCursorState() {
- return this._cursor.saveState();
- }
- restoreCursorState(states) {
- this._withViewEventsCollector(eventsCollector => this._cursor.restoreState(eventsCollector, states));
- }
- _executeCursorEdit(callback) {
- if (this._cursor.context.cursorConfig.readOnly) {
- // we cannot edit when read only...
- this._eventDispatcher.emitOutgoingEvent(new ReadOnlyEditAttemptEvent());
- return;
- }
- this._withViewEventsCollector(callback);
- }
- executeEdits(source, edits, cursorStateComputer) {
- this._executeCursorEdit(eventsCollector => this._cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer));
- }
- startComposition() {
- this._cursor.setIsDoingComposition(true);
- this._executeCursorEdit(eventsCollector => this._cursor.startComposition(eventsCollector));
- }
- endComposition(source) {
- this._cursor.setIsDoingComposition(false);
- this._executeCursorEdit(eventsCollector => this._cursor.endComposition(eventsCollector, source));
- }
- type(text, source) {
- this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source));
- }
- compositionType(text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source) {
- this._executeCursorEdit(eventsCollector => this._cursor.compositionType(eventsCollector, text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source));
- }
- paste(text, pasteOnNewLine, multicursorText, source) {
- this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source));
- }
- cut(source) {
- this._executeCursorEdit(eventsCollector => this._cursor.cut(eventsCollector, source));
- }
- executeCommand(command, source) {
- this._executeCursorEdit(eventsCollector => this._cursor.executeCommand(eventsCollector, command, source));
- }
- executeCommands(commands, source) {
- this._executeCursorEdit(eventsCollector => this._cursor.executeCommands(eventsCollector, commands, source));
- }
- revealPrimaryCursor(source, revealHorizontal) {
- this._withViewEventsCollector(eventsCollector => this._cursor.revealPrimary(eventsCollector, source, revealHorizontal, 0 /* Smooth */));
- }
- revealTopMostCursor(source) {
- 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, 0 /* Simple */, true, 0 /* Smooth */)));
- }
- revealBottomMostCursor(source) {
- 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, 0 /* Simple */, true, 0 /* Smooth */)));
- }
- revealRange(source, revealHorizontal, viewRange, verticalType, scrollType) {
- this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, verticalType, revealHorizontal, scrollType)));
- }
- //#endregion
- //#region viewLayout
- getVerticalOffsetForLineNumber(viewLineNumber) {
- return this.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber);
- }
- getScrollTop() {
- return this.viewLayout.getCurrentScrollTop();
- }
- setScrollTop(newScrollTop, scrollType) {
- this.viewLayout.setScrollPosition({ scrollTop: newScrollTop }, scrollType);
- }
- setScrollPosition(position, type) {
- this.viewLayout.setScrollPosition(position, type);
- }
- deltaScrollNow(deltaScrollLeft, deltaScrollTop) {
- this.viewLayout.deltaScrollNow(deltaScrollLeft, deltaScrollTop);
- }
- changeWhitespace(callback) {
- const hadAChange = this.viewLayout.changeWhitespace(callback);
- if (hadAChange) {
- this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewZonesChangedEvent());
- this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent());
- }
- }
- setMaxLineWidth(maxLineWidth) {
- this.viewLayout.setMaxLineWidth(maxLineWidth);
- }
- //#endregion
- _withViewEventsCollector(callback) {
- try {
- const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
- callback(eventsCollector);
- }
- finally {
- this._eventDispatcher.endEmitViewEvents();
- }
- }
- normalizePosition(position, affinity) {
- return this._lines.normalizePosition(position, affinity);
- }
- /**
- * Gets the column at which indentation stops at a given line.
- * @internal
- */
- getLineIndentColumn(lineNumber) {
- return this._lines.getLineIndentColumn(lineNumber);
- }
- }
- class OverviewRulerDecorations {
- constructor() {
- this._asMap = Object.create(null);
- this.asArray = [];
- }
- accept(color, zIndex, startLineNumber, endLineNumber, lane) {
- const prevGroup = this._asMap[color];
- if (prevGroup) {
- const prevData = prevGroup.data;
- const prevLane = prevData[prevData.length - 3];
- const prevEndLineNumber = prevData[prevData.length - 1];
- if (prevLane === lane && prevEndLineNumber + 1 >= startLineNumber) {
- // merge into prev
- if (endLineNumber > prevEndLineNumber) {
- prevData[prevData.length - 1] = endLineNumber;
- }
- return;
- }
- // push
- prevData.push(lane, startLineNumber, endLineNumber);
- }
- else {
- const group = new OverviewRulerDecorationsGroup(color, zIndex, [lane, startLineNumber, endLineNumber]);
- this._asMap[color] = group;
- this.asArray.push(group);
- }
- }
- }
|