
import { ArrayQueue } from '../../../base/common/arrays.js';
import { RunOnceScheduler } from '../../../base/common/async.js';
import { Color } from '../../../base/common/color.js';
import { Disposable } from '../../../base/common/lifecycle.js';
import { isWindows } from '../../../base/common/platform.js';
import { firstNonWhitespaceIndex, lastNonWhitespaceIndex } from '../../../base/common/strings.js';
import { EditorOption, filterValidationDecorations, filterFontDecorations } from '../config/editorOptions.js';
import { EDITOR_FONT_DEFAULTS } from '../config/fontInfo.js';
import { CursorsController } from '../cursor/cursor.js';
import { CursorConfiguration } from '../cursorCommon.js';
import { CursorChangeReason } from '../cursorEvents.js';
import { Position } from '../core/position.js';
import { Range } from '../core/range.js';
import { ScrollType } from '../editorCommon.js';
import { TextDirection, EndOfLinePreference, TrackedRangeStickiness } from '../model.js';
import { InternalModelContentChangeEvent, RawContentChangedType, ModelLineHeightChangedEvent, ModelFontChangedEvent } from '../textModelEvents.js';
import { TokenizationRegistry } from '../languages.js';
import { ColorId } from '../encodedTokenAttributes.js';
import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js';
import { tokenizeLineToHTML } from '../languages/textToHtmlTokenizer.js';
import { ViewScrollChangedEvent, ViewTokensColorsChangedEvent, ViewThemeChangedEvent, ViewFocusChangedEvent, ViewCompositionStartEvent, ViewCompositionEndEvent, ViewFlushedEvent, ViewLineMappingChangedEvent, ViewDecorationsChangedEvent, ViewConfigurationChangedEvent, ViewTokensChangedEvent, ViewLanguageConfigurationEvent, VerticalRevealType, ViewRevealRangeRequestEvent, ViewZonesChangedEvent } from '../viewEvents.js';
import { ViewLayout } from '../viewLayout/viewLayout.js';
import { MinimapTokensColorTracker } from './minimapTokensColorTracker.js';
import { ViewLineRenderingData, MinimapLinesRenderingData, OverviewRulerDecorationsGroup } from '../viewModel.js';
import { ViewModelDecorations } from './viewModelDecorations.js';
import { ViewModelEventDispatcher, ScrollChangedEvent, FocusChangedEvent, WidgetFocusChangedEvent, ModelContentChangedEvent, ModelLineHeightChangedEvent as ModelLineHeightChangedEvent$1, ModelFontChangedEvent as ModelFontChangedEvent$1, ModelTokensChangedEvent, ModelLanguageConfigurationChangedEvent, ModelLanguageChangedEvent, ModelOptionsChangedEvent, ModelDecorationsChangedEvent, HiddenAreasChangedEvent, ReadOnlyEditAttemptEvent, ViewZonesChangedEvent as ViewZonesChangedEvent$1 } from '../viewModelEventDispatcher.js';
import { ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from './viewModelLines.js';
import { GlyphMarginLanesModel } from './glyphLanesModel.js';

class ViewModel extends Disposable {
    constructor(
        editorId,
        configuration,
        model,
        domLineBreaksComputerFactory,
        monospaceLineBreaksComputerFactory,
        scheduleAtNextAnimationFrame,
        languageConfigurationService,
        _themeService,
        _attachedView,
        _transactionalTarget
    ) {
        super();
        this.languageConfigurationService = languageConfigurationService;
        this._themeService = _themeService;
        this._attachedView = _attachedView;
        this._transactionalTarget = _transactionalTarget;
        this.hiddenAreasModel = ( new HiddenAreasModel());
        this.previousHiddenAreas = [];
        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.languageConfigurationService
        ));
        this._updateConfigurationViewLineCount = this._register(( new RunOnceScheduler(() => this._updateConfigurationViewLineCountNow(), 0)));
        this._hasFocus = false;
        this._viewportStart = ViewportStart.create(this.model);
        this.glyphLanes = ( new GlyphMarginLanesModel(0));
        if (this.model.isTooLargeForTokenization()) {
            this._lines = ( new ViewModelLinesFromModelAsIs(this.model));
        } else {
            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);
            const wordBreak = options.get(EditorOption.wordBreak);
            const wrapOnEscapedLineFeeds = options.get(EditorOption.wrapOnEscapedLineFeeds);
            this._lines = ( new ViewModelLinesFromProjectedModel(
                this._editorId,
                this.model,
                domLineBreaksComputerFactory,
                monospaceLineBreaksComputerFactory,
                fontInfo,
                this.model.getOptions().tabSize,
                wrappingStrategy,
                wrappingInfo.wrappingColumn,
                wrappingIndent,
                wordBreak,
                wrapOnEscapedLineFeeds
            ));
        }
        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(),
            this._getCustomLineHeights(),
            scheduleAtNextAnimationFrame
        )));
        this._register(this.viewLayout.onDidScroll(e => {
            if (e.scrollTopChanged) {
                this._handleVisibleLinesChanged();
            }
            if (e.scrollTopChanged) {
                this._viewportStart.invalidate();
            }
            this._eventDispatcher.emitSingleViewEvent(new 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 ViewTokensColorsChangedEvent());
        }));
        this._register(this._themeService.onDidColorThemeChange(theme => {
            this._invalidateDecorationsColorCache();
            this._eventDispatcher.emitSingleViewEvent(new ViewThemeChangedEvent(theme));
        }));
        this._updateConfigurationViewLineCountNow();
    }
    dispose() {
        super.dispose();
        this._decorations.dispose();
        this._lines.dispose();
        this._viewportStart.dispose();
        this._eventDispatcher.dispose();
    }
    getEditorOption(id) {
        return this._configuration.options.get(id);
    }
    createLineBreaksComputer() {
        return this._lines.createLineBreaksComputer();
    }
    addViewEventHandler(eventHandler) {
        this._eventDispatcher.addViewEventHandler(eventHandler);
    }
    removeViewEventHandler(eventHandler) {
        this._eventDispatcher.removeViewEventHandler(eventHandler);
    }
    _getCustomLineHeights() {
        const allowVariableLineHeights = this._configuration.options.get(EditorOption.allowVariableLineHeights);
        if (!allowVariableLineHeights) {
            return [];
        }
        const defaultLineHeight = this._configuration.options.get(EditorOption.lineHeight);
        const decorations = this.model.getCustomLineHeightsDecorations(this._editorId);
        return ( decorations.map(d => {
            const lineNumber = d.range.startLineNumber;
            const viewRange = this.coordinatesConverter.convertModelRangeToViewRange(( new Range(lineNumber, 1, lineNumber, this.model.getLineMaxColumn(lineNumber))));
            return {
                decorationId: d.id,
                startLineNumber: viewRange.startLineNumber,
                endLineNumber: viewRange.endLineNumber,
                lineHeight: d.options.lineHeight ? d.options.lineHeight * defaultLineHeight : 0
            };
        }));
    }
    _updateConfigurationViewLineCountNow() {
        this._configuration.setViewLineCount(this._lines.getViewLineCount());
    }
    getModelVisibleRanges() {
        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);
        return modelVisibleRanges;
    }
    visibleLinesStabilized() {
        const modelVisibleRanges = this.getModelVisibleRanges();
        this._attachedView.setVisibleLines(modelVisibleRanges, true);
    }
    _handleVisibleLinesChanged() {
        const modelVisibleRanges = this.getModelVisibleRanges();
        this._attachedView.setVisibleLines(modelVisibleRanges, false);
    }
    setHasFocus(hasFocus) {
        this._hasFocus = hasFocus;
        this._cursor.setHasFocus(hasFocus);
        this._eventDispatcher.emitSingleViewEvent(new ViewFocusChangedEvent(hasFocus));
        this._eventDispatcher.emitOutgoingEvent(( new FocusChangedEvent(!hasFocus, hasFocus)));
    }
    setHasWidgetFocus(hasWidgetFocus) {
        this._eventDispatcher.emitOutgoingEvent(( new WidgetFocusChangedEvent(!hasWidgetFocus, hasWidgetFocus)));
    }
    onCompositionStart() {
        this._eventDispatcher.emitSingleViewEvent(new ViewCompositionStartEvent());
    }
    onCompositionEnd() {
        this._eventDispatcher.emitSingleViewEvent(new ViewCompositionEndEvent());
    }
    _captureStableViewport() {
        if (this._viewportStart.isValid && this.viewLayout.getCurrentScrollTop() > 0) {
            const previousViewportStartViewPosition = ( new Position(
                this._viewportStart.viewLineNumber,
                this.getLineMinColumn(this._viewportStart.viewLineNumber)
            ));
            const previousViewportStartModelPosition = this.coordinatesConverter.convertViewPositionToModelPosition(previousViewportStartViewPosition);
            return ( new StableViewport(previousViewportStartModelPosition, this._viewportStart.startLineDelta));
        }
        return ( new StableViewport(null, 0));
    }
    _onConfigurationChanged(eventsCollector, e) {
        const stableViewport = this._captureStableViewport();
        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);
        const wordBreak = options.get(EditorOption.wordBreak);
        if (this._lines.setWrappingSettings(
            fontInfo,
            wrappingStrategy,
            wrappingInfo.wrappingColumn,
            wrappingIndent,
            wordBreak
        )) {
            eventsCollector.emitViewEvent(new ViewFlushedEvent());
            eventsCollector.emitViewEvent(new ViewLineMappingChangedEvent());
            eventsCollector.emitViewEvent(new ViewDecorationsChangedEvent(null));
            this._cursor.onLineMappingChanged(eventsCollector);
            this._decorations.onLineMappingChanged();
            this.viewLayout.onFlushed(this.getLineCount(), this._getCustomLineHeights());
            this._updateConfigurationViewLineCount.schedule();
        }
        if (e.hasChanged(EditorOption.readOnly)) {
            this._decorations.reset();
            eventsCollector.emitViewEvent(new ViewDecorationsChangedEvent(null));
        }
        if (e.hasChanged(EditorOption.renderValidationDecorations)) {
            this._decorations.reset();
            eventsCollector.emitViewEvent(new ViewDecorationsChangedEvent(null));
        }
        eventsCollector.emitViewEvent(new ViewConfigurationChangedEvent(e));
        this.viewLayout.onConfigurationChanged(e);
        stableViewport.recoverViewportStart(this.coordinatesConverter, this.viewLayout);
        if (CursorConfiguration.shouldRecreate(e)) {
            this.cursorConfig = ( new CursorConfiguration(
                this.model.getLanguageId(),
                this.model.getOptions(),
                this._configuration,
                this.languageConfigurationService
            ));
            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 instanceof InternalModelContentChangeEvent ? e.rawContentChangedEvent.changes : e.changes);
                const versionId = (e instanceof InternalModelContentChangeEvent ? e.rawContentChangedEvent.versionId : null);
                const lineBreaksComputer = this._lines.createLineBreaksComputer();
                for (const change of changes) {
                    switch (change.changeType) {
                    case RawContentChangedType.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 RawContentChangedType.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 RawContentChangedType.Flush:
                        {
                            this._lines.onModelFlushed();
                            eventsCollector.emitViewEvent(new ViewFlushedEvent());
                            this._decorations.reset();
                            this.viewLayout.onFlushed(this.getLineCount(), this._getCustomLineHeights());
                            hadOtherModelChange = true;
                            break;
                        }
                    case RawContentChangedType.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 RawContentChangedType.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 RawContentChangedType.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 RawContentChangedType.EOLChanged:
                        {
                            break;
                        }
                    }
                }
                if (versionId !== null) {
                    this._lines.acceptVersionId(versionId);
                }
                this.viewLayout.onHeightMaybeChanged();
                if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
                    eventsCollector.emitViewEvent(new ViewLineMappingChangedEvent());
                    eventsCollector.emitViewEvent(new ViewDecorationsChangedEvent(null));
                    this._cursor.onLineMappingChanged(eventsCollector);
                    this._decorations.onLineMappingChanged();
                }
            } finally {
                this._eventDispatcher.endEmitViewEvents();
            }
            const viewportStartWasValid = this._viewportStart.isValid;
            this._viewportStart.invalidate();
            this._configuration.setModelLineCount(this.model.getLineCount());
            this._updateConfigurationViewLineCountNow();
            if (!this._hasFocus && this.model.getAttachedEditorCount() >= 2 && viewportStartWasValid) {
                const modelRange = this.model._getTrackedRange(this._viewportStart.modelTrackedRange);
                if (modelRange) {
                    const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelRange.getStartPosition());
                    const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
                    this.viewLayout.setScrollPosition({
                        scrollTop: viewPositionTop + this._viewportStart.startLineDelta
                    }, ScrollType.Immediate);
                }
            }
            try {
                const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
                if (e instanceof InternalModelContentChangeEvent) {
                    eventsCollector.emitOutgoingEvent(( new ModelContentChangedEvent(e.contentChangedEvent)));
                }
                this._cursor.onModelContentChanged(eventsCollector, e);
            } finally {
                this._eventDispatcher.endEmitViewEvents();
            }
            this._handleVisibleLinesChanged();
        }));
        const allowVariableLineHeights = this._configuration.options.get(EditorOption.allowVariableLineHeights);
        if (allowVariableLineHeights) {
            this._register(this.model.onDidChangeLineHeight(e => {
                const filteredChanges = e.changes.filter(change => change.ownerId === this._editorId || change.ownerId === 0);
                this.viewLayout.changeSpecialLineHeights(accessor => {
                    for (const change of filteredChanges) {
                        const {
                            decorationId,
                            lineNumber,
                            lineHeightMultiplier
                        } = change;
                        const viewRange = this.coordinatesConverter.convertModelRangeToViewRange(( new Range(lineNumber, 1, lineNumber, this.model.getLineMaxColumn(lineNumber))));
                        if (lineHeightMultiplier !== null) {
                            accessor.insertOrChangeCustomLineHeight(
                                decorationId,
                                viewRange.startLineNumber,
                                viewRange.endLineNumber,
                                lineHeightMultiplier * this._configuration.options.get(EditorOption.lineHeight)
                            );
                        } else {
                            accessor.removeCustomLineHeight(decorationId);
                        }
                    }
                });
                if (filteredChanges.length > 0) {
                    const filteredEvent = new ModelLineHeightChangedEvent(filteredChanges);
                    this._eventDispatcher.emitOutgoingEvent(( new ModelLineHeightChangedEvent$1(filteredEvent)));
                }
            }));
        }
        const allowVariableFonts = this._configuration.options.get(EditorOption.effectiveAllowVariableFonts);
        if (allowVariableFonts) {
            this._register(this.model.onDidChangeFont(e => {
                const filteredChanges = e.changes.filter(change => change.ownerId === this._editorId || change.ownerId === 0);
                if (filteredChanges.length > 0) {
                    const filteredEvent = new ModelFontChangedEvent(filteredChanges);
                    this._eventDispatcher.emitOutgoingEvent(( new ModelFontChangedEvent$1(filteredEvent)));
                }
            }));
        }
        this._register(this.model.onDidChangeTokens(e => {
            const 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 ViewTokensChangedEvent(viewRanges));
            this._eventDispatcher.emitOutgoingEvent(( new ModelTokensChangedEvent(e)));
        }));
        this._register(this.model.onDidChangeLanguageConfiguration(e => {
            this._eventDispatcher.emitSingleViewEvent(new ViewLanguageConfigurationEvent());
            this.cursorConfig = ( new CursorConfiguration(
                this.model.getLanguageId(),
                this.model.getOptions(),
                this._configuration,
                this.languageConfigurationService
            ));
            this._cursor.updateConfiguration(this.cursorConfig);
            this._eventDispatcher.emitOutgoingEvent(( new ModelLanguageConfigurationChangedEvent(e)));
        }));
        this._register(this.model.onDidChangeLanguage(e => {
            this.cursorConfig = ( new CursorConfiguration(
                this.model.getLanguageId(),
                this.model.getOptions(),
                this._configuration,
                this.languageConfigurationService
            ));
            this._cursor.updateConfiguration(this.cursorConfig);
            this._eventDispatcher.emitOutgoingEvent(( new ModelLanguageChangedEvent(e)));
        }));
        this._register(this.model.onDidChangeOptions(e => {
            if (this._lines.setTabSize(this.model.getOptions().tabSize)) {
                try {
                    const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
                    eventsCollector.emitViewEvent(new ViewFlushedEvent());
                    eventsCollector.emitViewEvent(new ViewLineMappingChangedEvent());
                    eventsCollector.emitViewEvent(new ViewDecorationsChangedEvent(null));
                    this._cursor.onLineMappingChanged(eventsCollector);
                    this._decorations.onLineMappingChanged();
                    this.viewLayout.onFlushed(this.getLineCount(), this._getCustomLineHeights());
                } finally {
                    this._eventDispatcher.endEmitViewEvents();
                }
                this._updateConfigurationViewLineCount.schedule();
            }
            this.cursorConfig = ( new CursorConfiguration(
                this.model.getLanguageId(),
                this.model.getOptions(),
                this._configuration,
                this.languageConfigurationService
            ));
            this._cursor.updateConfiguration(this.cursorConfig);
            this._eventDispatcher.emitOutgoingEvent(( new ModelOptionsChangedEvent(e)));
        }));
        this._register(this.model.onDidChangeDecorations(e => {
            this._decorations.onModelDecorationsChanged();
            this._eventDispatcher.emitSingleViewEvent(new ViewDecorationsChangedEvent(e));
            this._eventDispatcher.emitOutgoingEvent(( new ModelDecorationsChangedEvent(e)));
        }));
    }
    getFontSizeAtPosition(position) {
        const allowVariableFonts = this._configuration.options.get(EditorOption.effectiveAllowVariableFonts);
        if (!allowVariableFonts) {
            return null;
        }
        const fontDecorations = this.model.getFontDecorationsInRange(Range.fromPositions(position), this._editorId);
        let fontSize = this._configuration.options.get(EditorOption.fontInfo).fontSize + "px";
        for (const fontDecoration of fontDecorations) {
            if (fontDecoration.options.fontSize) {
                fontSize = fontDecoration.options.fontSize;
                break;
            }
        }
        return fontSize;
    }
    setHiddenAreas(ranges, source, forceUpdate) {
        this.hiddenAreasModel.setHiddenAreas(source, ranges);
        const mergedRanges = this.hiddenAreasModel.getMergedRanges();
        if (mergedRanges === this.previousHiddenAreas && !forceUpdate) {
            return;
        }
        this.previousHiddenAreas = mergedRanges;
        const stableViewport = this._captureStableViewport();
        let lineMappingChanged = false;
        try {
            const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
            lineMappingChanged = this._lines.setHiddenAreas(mergedRanges);
            if (lineMappingChanged) {
                eventsCollector.emitViewEvent(new ViewFlushedEvent());
                eventsCollector.emitViewEvent(new ViewLineMappingChangedEvent());
                eventsCollector.emitViewEvent(new ViewDecorationsChangedEvent(null));
                this._cursor.onLineMappingChanged(eventsCollector);
                this._decorations.onLineMappingChanged();
                this.viewLayout.onFlushed(this.getLineCount(), this._getCustomLineHeights());
                this.viewLayout.onHeightMaybeChanged();
            }
            const firstModelLineInViewPort = stableViewport.viewportStartModelPosition?.lineNumber;
            const firstModelLineIsHidden = firstModelLineInViewPort && ( mergedRanges.some(
                range => range.startLineNumber <= firstModelLineInViewPort && firstModelLineInViewPort <= range.endLineNumber
            ));
            if (!firstModelLineIsHidden) {
                stableViewport.recoverViewportStart(this.coordinatesConverter, this.viewLayout);
            }
        } finally {
            this._eventDispatcher.endEmitViewEvents();
        }
        this._updateConfigurationViewLineCount.schedule();
        if (lineMappingChanged) {
            this._eventDispatcher.emitOutgoingEvent(( new HiddenAreasChangedEvent()));
        }
    }
    getVisibleRangesPlusViewportAboveBelow() {
        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);
        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);
    }
    getHiddenAreas() {
        return this._lines.getHiddenAreas();
    }
    _toModelVisibleRanges(visibleViewRange) {
        const visibleRange = this.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
        const hiddenAreas = this._lines.getHiddenAreas();
        if (hiddenAreas.length === 0) {
            return [visibleRange];
        }
        const result = [];
        let resultLen = 0;
        let startLineNumber = visibleRange.startLineNumber;
        let startColumn = visibleRange.startColumn;
        const endLineNumber = visibleRange.endLineNumber;
        const 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)
        ));
    }
    getViewRangeWithCursorPadding(viewRange) {
        const options = this._configuration.options;
        const cursorSurroundingLines = options.get(EditorOption.cursorSurroundingLines);
        const stickyScroll = options.get(EditorOption.stickyScroll);
        let {
            startLineNumber,
            endLineNumber
        } = viewRange;
        const padding = Math.min(Math.max(
            cursorSurroundingLines,
            stickyScroll.enabled ? stickyScroll.maxLineCount : 0
        ), Math.floor((endLineNumber - startLineNumber + 1) / 2));
        startLineNumber += padding;
        endLineNumber -= Math.max(0, padding - 1);
        if (padding === 0 || startLineNumber > endLineNumber) {
            return viewRange;
        }
        return ( new Range(
            startLineNumber,
            this.getLineMinColumn(startLineNumber),
            endLineNumber,
            this.getLineMaxColumn(endLineNumber)
        ));
    }
    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") {
            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;
    }
    getLineCount() {
        return this._lines.getViewLineCount();
    }
    setViewport(startLineNumber, endLineNumber, centeredLineNumber) {
        if (this._lines.getViewLineCount() === 0) {
            return;
        }
        this._viewportStart.update(this, startLineNumber);
    }
    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 = firstNonWhitespaceIndex(this.getLineContent(lineNumber));
        if (result === -1) {
            return 0;
        }
        return result + 1;
    }
    getLineLastNonWhitespaceColumn(lineNumber) {
        const result = lastNonWhitespaceIndex(this.getLineContent(lineNumber));
        if (result === -1) {
            return 0;
        }
        return result + 2;
    }
    getMinimapDecorationsInRange(range) {
        return this._decorations.getMinimapDecorationsInRange(range);
    }
    getDecorationsInViewport(visibleRange) {
        return this._decorations.getDecorationsViewportData(visibleRange).decorations;
    }
    getInjectedTextAt(viewPosition) {
        return this._lines.getInjectedTextAt(viewPosition);
    }
    _getTextDirection(lineNumber, decorations) {
        let rtlCount = 0;
        for (const decoration of decorations) {
            const range = decoration.range;
            if (range.startLineNumber > lineNumber || range.endLineNumber < lineNumber) {
                continue;
            }
            const textDirection = decoration.options.textDirection;
            if (textDirection === TextDirection.RTL) {
                rtlCount++;
            } else if (textDirection === TextDirection.LTR) {
                rtlCount--;
            }
        }
        return rtlCount > 0 ? TextDirection.RTL : TextDirection.LTR;
    }
    getTextDirection(lineNumber) {
        const decorationsCollection = this._decorations.getDecorationsOnLine(lineNumber);
        return this._getTextDirection(lineNumber, decorationsCollection.decorations);
    }
    getViewportViewLineRenderingData(visibleRange, lineNumber) {
        const viewportDecorationsCollection = this._decorations.getDecorationsViewportData(visibleRange);
        const relativeLineNumber = lineNumber - visibleRange.startLineNumber;
        const inlineDecorations = viewportDecorationsCollection.inlineDecorations[relativeLineNumber];
        const hasVariableFonts = viewportDecorationsCollection.hasVariableFonts[relativeLineNumber];
        return this._getViewLineRenderingData(
            lineNumber,
            inlineDecorations,
            hasVariableFonts,
            viewportDecorationsCollection.decorations
        );
    }
    getViewLineRenderingData(lineNumber) {
        const decorationsCollection = this._decorations.getDecorationsOnLine(lineNumber);
        return this._getViewLineRenderingData(
            lineNumber,
            decorationsCollection.inlineDecorations[0],
            decorationsCollection.hasVariableFonts[0],
            decorationsCollection.decorations
        );
    }
    _getViewLineRenderingData(lineNumber, inlineDecorations, hasVariableFonts, decorations) {
        const mightContainRTL = this.model.mightContainRTL();
        const mightContainNonBasicASCII = this.model.mightContainNonBasicASCII();
        const tabSize = this.getTabSize();
        const lineData = this._lines.getViewLineData(lineNumber);
        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,
            this._getTextDirection(lineNumber, decorations),
            hasVariableFonts
        ));
    }
    getViewLineData(lineNumber) {
        return this._lines.getViewLineData(lineNumber);
    }
    getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed) {
        const 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),
            filterFontDecorations(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.value);
            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;
    }
    _invalidateDecorationsColorCache() {
        const decorations = this.model.getOverviewRulerDecorations();
        for (const decoration of decorations) {
            const opts1 = decoration.options.overviewRuler;
            opts1?.invalidateCachedColor();
            const opts2 = decoration.options.minimap;
            opts2?.invalidateCachedColor();
        }
    }
    getValueInRange(range, eol) {
        const modelRange = this.coordinatesConverter.convertViewRangeToModelRange(range);
        return this.model.getValueInRange(modelRange, eol);
    }
    getValueLengthInRange(range, eol) {
        const modelRange = this.coordinatesConverter.convertViewRangeToModelRange(range);
        return this.model.getValueLengthInRange(modelRange, eol);
    }
    modifyPosition(position, offset) {
        const modelPosition = this.coordinatesConverter.convertViewPositionToModelPosition(position);
        const resultModelPosition = this.model.modifyPosition(modelPosition, offset);
        return this.coordinatesConverter.convertModelPositionToViewPosition(resultModelPosition);
    }
    deduceModelPositionRelativeToViewPosition(viewAnchorPosition, deltaOffset, lineFeedCnt) {
        const modelAnchor = this.coordinatesConverter.convertViewPositionToModelPosition(viewAnchorPosition);
        if (this.model.getEOL().length === 2) {
            if (deltaOffset < 0) {
                deltaOffset -= lineFeedCnt;
            } else {
                deltaOffset += lineFeedCnt;
            }
        }
        const modelAnchorOffset = this.model.getOffsetAt(modelAnchor);
        const resultOffset = modelAnchorOffset + deltaOffset;
        return this.model.getPositionAt(resultOffset);
    }
    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 && !emptySelectionClipboard) {
            return {
                sourceRanges: [],
                sourceText: ""
            };
        }
        const ranges = [];
        const result = [];
        const pushRange = (modelRange, append = "") => {
            ranges.push(modelRange);
            result.push(this.model.getValueInRange(
                modelRange,
                forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined
            ) + append);
        };
        if (hasEmptyRange && emptySelectionClipboard) {
            let prevModelLineNumber = 0;
            for (const modelRange of modelRanges) {
                const modelLineNumber = modelRange.startLineNumber;
                if (modelRange.isEmpty()) {
                    if (modelLineNumber !== prevModelLineNumber) {
                        pushRange(( new Range(
                            modelLineNumber,
                            this.model.getLineMinColumn(modelLineNumber),
                            modelLineNumber,
                            this.model.getLineMaxColumn(modelLineNumber)
                        )), newLineCharacter);
                    }
                } else {
                    pushRange(modelRange);
                }
                prevModelLineNumber = modelLineNumber;
            }
        } else {
            for (const modelRange of modelRanges) {
                if (!modelRange.isEmpty()) {
                    pushRange(modelRange);
                }
            }
        }
        return {
            sourceRanges: ranges,
            sourceText: result.length === 1 ? result[0] : result
        };
    }
    getRichTextToCopy(modelRanges, emptySelectionClipboard) {
        const languageId = this.model.getLanguageId();
        if (languageId === PLAINTEXT_LANGUAGE_ID) {
            return null;
        }
        if (modelRanges.length !== 1) {
            return null;
        }
        let range = modelRanges[0];
        if (range.isEmpty()) {
            if (!emptySelectionClipboard) {
                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(EditorOption.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[ColorId.DefaultForeground]};` + `background-color: ${colorMap[ColorId.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.tokenization.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,
                    isWindows
                );
            }
        }
        return result;
    }
    _getColorMap() {
        const colorMap = TokenizationRegistry.getColorMap();
        const result = ["#000000"];
        if (colorMap) {
            for (let i = 1, len = colorMap.length; i < len; i++) {
                result[i] = Color.Format.CSS.formatHex(colorMap[i]);
            }
        }
        return result;
    }
    getPrimaryCursorState() {
        return this._cursor.getPrimaryCursorState();
    }
    getLastAddedCursorIndex() {
        return this._cursor.getLastAddedCursorIndex();
    }
    getCursorStates() {
        return this._cursor.getCursorStates();
    }
    setCursorStates(source, reason, states) {
        return 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 = CursorChangeReason.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) {
            this._eventDispatcher.emitOutgoingEvent(( new ReadOnlyEditAttemptEvent()));
            return;
        }
        this._withViewEventsCollector(callback);
    }
    executeEdits(source, edits, cursorStateComputer, reason) {
        this._executeCursorEdit(
            eventsCollector => this._cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer, reason)
        );
    }
    startComposition() {
        this._executeCursorEdit(eventsCollector => this._cursor.startComposition(eventsCollector));
    }
    endComposition(source) {
        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)
        );
    }
    revealAllCursors(source, revealHorizontal, minimalReveal = false) {
        this._withViewEventsCollector(eventsCollector => this._cursor.revealAll(
            eventsCollector,
            source,
            minimalReveal,
            VerticalRevealType.Simple,
            revealHorizontal,
            ScrollType.Smooth
        ));
    }
    revealPrimaryCursor(source, revealHorizontal, minimalReveal = false) {
        this._withViewEventsCollector(eventsCollector => this._cursor.revealPrimary(
            eventsCollector,
            source,
            minimalReveal,
            VerticalRevealType.Simple,
            revealHorizontal,
            ScrollType.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 ViewRevealRangeRequestEvent(
                source,
                false,
                viewRange,
                null,
                VerticalRevealType.Simple,
                true,
                ScrollType.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 ViewRevealRangeRequestEvent(
                source,
                false,
                viewRange,
                null,
                VerticalRevealType.Simple,
                true,
                ScrollType.Smooth
            ))
        );
    }
    revealRange(source, revealHorizontal, viewRange, verticalType, scrollType) {
        this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(
            new ViewRevealRangeRequestEvent(source, false, viewRange, null, verticalType, revealHorizontal, scrollType)
        ));
    }
    changeWhitespace(callback) {
        const hadAChange = this.viewLayout.changeWhitespace(callback);
        if (hadAChange) {
            this._eventDispatcher.emitSingleViewEvent(new ViewZonesChangedEvent());
            this._eventDispatcher.emitOutgoingEvent(( new ViewZonesChangedEvent$1()));
        }
    }
    _withViewEventsCollector(callback) {
        return this._transactionalTarget.batchChanges(() => {
            try {
                const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
                return callback(eventsCollector);
            } finally {
                this._eventDispatcher.endEmitViewEvents();
            }
        });
    }
    batchEvents(callback) {
        this._withViewEventsCollector(() => {
            callback();
        });
    }
    normalizePosition(position, affinity) {
        return this._lines.normalizePosition(position, affinity);
    }
    getLineIndentColumn(lineNumber) {
        return this._lines.getLineIndentColumn(lineNumber);
    }
}
class ViewportStart {
    static create(model) {
        const viewportStartLineTrackedRange = model._setTrackedRange(null, ( new Range(1, 1, 1, 1)), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
        return ( new ViewportStart(model, 1, false, viewportStartLineTrackedRange, 0));
    }
    get viewLineNumber() {
        return this._viewLineNumber;
    }
    get isValid() {
        return this._isValid;
    }
    get modelTrackedRange() {
        return this._modelTrackedRange;
    }
    get startLineDelta() {
        return this._startLineDelta;
    }
    constructor(_model, _viewLineNumber, _isValid, _modelTrackedRange, _startLineDelta) {
        this._model = _model;
        this._viewLineNumber = _viewLineNumber;
        this._isValid = _isValid;
        this._modelTrackedRange = _modelTrackedRange;
        this._startLineDelta = _startLineDelta;
    }
    dispose() {
        this._model._setTrackedRange(
            this._modelTrackedRange,
            null,
            TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
        );
    }
    update(viewModel, startLineNumber) {
        const position = viewModel.coordinatesConverter.convertViewPositionToModelPosition(( new Position(startLineNumber, viewModel.getLineMinColumn(startLineNumber))));
        const viewportStartLineTrackedRange = viewModel.model._setTrackedRange(this._modelTrackedRange, ( new Range(position.lineNumber, position.column, position.lineNumber, position.column)), TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
        const viewportStartLineTop = viewModel.viewLayout.getVerticalOffsetForLineNumber(startLineNumber);
        const scrollTop = viewModel.viewLayout.getCurrentScrollTop();
        this._viewLineNumber = startLineNumber;
        this._isValid = true;
        this._modelTrackedRange = viewportStartLineTrackedRange;
        this._startLineDelta = scrollTop - viewportStartLineTop;
    }
    invalidate() {
        this._isValid = false;
    }
}
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) {
                if (endLineNumber > prevEndLineNumber) {
                    prevData[prevData.length - 1] = endLineNumber;
                }
                return;
            }
            prevData.push(lane, startLineNumber, endLineNumber);
        } else {
            const group = ( new OverviewRulerDecorationsGroup(color, zIndex, [lane, startLineNumber, endLineNumber]));
            this._asMap[color] = group;
            this.asArray.push(group);
        }
    }
}
class HiddenAreasModel {
    constructor() {
        this.hiddenAreas = ( new Map());
        this.shouldRecompute = false;
        this.ranges = [];
    }
    setHiddenAreas(source, ranges) {
        const existing = this.hiddenAreas.get(source);
        if (existing && rangeArraysEqual(existing, ranges)) {
            return;
        }
        this.hiddenAreas.set(source, ranges);
        this.shouldRecompute = true;
    }
    getMergedRanges() {
        if (!this.shouldRecompute) {
            return this.ranges;
        }
        this.shouldRecompute = false;
        const newRanges = Array.from(( this.hiddenAreas.values())).reduce((r, hiddenAreas) => mergeLineRangeArray(r, hiddenAreas), []);
        if (rangeArraysEqual(this.ranges, newRanges)) {
            return this.ranges;
        }
        this.ranges = newRanges;
        return this.ranges;
    }
}
function mergeLineRangeArray(arr1, arr2) {
    const result = [];
    let i = 0;
    let j = 0;
    while (i < arr1.length && j < arr2.length) {
        const item1 = arr1[i];
        const item2 = arr2[j];
        if (item1.endLineNumber < item2.startLineNumber - 1) {
            result.push(arr1[i++]);
        } else if (item2.endLineNumber < item1.startLineNumber - 1) {
            result.push(arr2[j++]);
        } else {
            const startLineNumber = Math.min(item1.startLineNumber, item2.startLineNumber);
            const endLineNumber = Math.max(item1.endLineNumber, item2.endLineNumber);
            result.push(( new Range(startLineNumber, 1, endLineNumber, 1)));
            i++;
            j++;
        }
    }
    while (i < arr1.length) {
        result.push(arr1[i++]);
    }
    while (j < arr2.length) {
        result.push(arr2[j++]);
    }
    return result;
}
function rangeArraysEqual(arr1, arr2) {
    if (arr1.length !== arr2.length) {
        return false;
    }
    for (let i = 0; i < arr1.length; i++) {
        if (!arr1[i].equalsRange(arr2[i])) {
            return false;
        }
    }
    return true;
}
class StableViewport {
    constructor(viewportStartModelPosition, startLineDelta) {
        this.viewportStartModelPosition = viewportStartModelPosition;
        this.startLineDelta = startLineDelta;
    }
    recoverViewportStart(coordinatesConverter, viewLayout) {
        if (!this.viewportStartModelPosition) {
            return;
        }
        const viewPosition = coordinatesConverter.convertModelPositionToViewPosition(this.viewportStartModelPosition);
        const viewPositionTop = viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
        viewLayout.setScrollPosition({
            scrollTop: viewPositionTop + this.startLineDelta
        }, ScrollType.Immediate);
    }
}

export { ViewModel };
