
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { addDisposableListener, isKeyboardEvent } from '../../../../base/browser/dom.js';
import { DomEmitter } from '../../../../base/browser/event.js';
import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';
import { RunOnceScheduler } from '../../../../base/common/async.js';
import { CancellationTokenSource, CancellationToken } from '../../../../base/common/cancellation.js';
import { memoize } from '../../../../base/common/decorators.js';
import { onUnexpectedExternalError, illegalArgument } from '../../../../base/common/errors.js';
import { Event } from '../../../../base/common/event.js';
import { visit } from '../../../../base/common/json.js';
import { setProperty } from '../../../../base/common/jsonEdit.js';
import { KeyCode } from '../../../../base/common/keyCodes.js';
import { MutableDisposable, DisposableStore, toDisposable, dispose } from '../../../../base/common/lifecycle.js';
import { clamp } from '../../../../base/common/numbers.js';
import { basename } from '../../../../base/common/path.js';
import { isMacintosh } from '../../../../base/common/platform.js';
import { noBreakWhitespace, format } from '../../../../base/common/strings.js';
import { isDefined, assertType } from '../../../../base/common/types.js';
import { Constants } from '../../../../base/common/uint.js';
import { URI } from '../../../../base/common/uri.js';
import { CoreEditingCommands } from '../../../../editor/browser/coreCommands.js';
import { MouseTargetType } from '../../../../editor/browser/editorBrowser.js';
import { EditorOption } from '../../../../editor/common/config/editorOptions.js';
import { EditOperation } from '../../../../editor/common/core/editOperation.js';
import { Position } from '../../../../editor/common/core/position.js';
import { Range } from '../../../../editor/common/core/range.js';
import { DEFAULT_WORD_REGEXP } from '../../../../editor/common/core/wordHelper.js';
import { ScrollType } from '../../../../editor/common/editorCommon.js';
import { StandardTokenType } from '../../../../editor/common/encodedTokenAttributes.js';
import { InjectedTextCursorStops } from '../../../../editor/common/model.js';
import { ILanguageFeatureDebounceService } from '../../../../editor/common/services/languageFeatureDebounce.service.js';
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.service.js';
import { IModelService } from '../../../../editor/common/services/model.service.js';
import { ContentHoverController } from '../../../../editor/contrib/hover/browser/contentHoverController.js';
import { HoverStartMode, HoverStartSource } from '../../../../editor/contrib/hover/browser/hoverOperation.js';
import { localize } from '../../../../nls.js';
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
import { ICommandService } from '../../../../platform/commands/common/commands.service.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.service.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { registerColor } from '../../../../platform/theme/common/colorUtils.js';
import '../../../../platform/theme/common/colors/baseColors.js';
import '../../../../platform/theme/common/colors/chartsColors.js';
import '../../../../platform/theme/common/colors/editorColors.js';
import '../../../../platform/theme/common/colors/inputColors.js';
import '../../../../platform/theme/common/colors/listColors.js';
import '../../../../platform/theme/common/colors/menuColors.js';
import '../../../../platform/theme/common/colors/minimapColors.js';
import '../../../../platform/theme/common/colors/miscColors.js';
import '../../../../platform/theme/common/colors/quickpickColors.js';
import '../../../../platform/theme/common/colors/searchColors.js';
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.service.js';
import { DebugHoverWidget, ShowDebugHoverResult } from './debugHover.js';
import { ExceptionWidget } from './exceptionWidget.js';
import { CONTEXT_EXCEPTION_WIDGET_VISIBLE, State } from '../common/debug.js';
import { IDebugService } from '../common/debug.service.js';
import { Expression } from '../common/debugModel.js';
import { IHostService } from '../../../services/host/browser/host.service.js';
import { IEditorService } from '../../../services/editor/common/editorService.service.js';
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { InsertLineAfterAction } from '../../../../editor/contrib/linesOperations/browser/linesOperations.js';

const MAX_NUM_INLINE_VALUES = 100;
const MAX_INLINE_DECORATOR_LENGTH = 150;
const MAX_TOKENIZATION_LINE_LEN = 500;
const DEAFULT_INLINE_DEBOUNCE_DELAY = 200;
registerColor("editor.inlineValuesForeground", {
    dark: "#ffffff80",
    light: "#00000080",
    hcDark: "#ffffff80",
    hcLight: "#00000080"
}, ( localize(8323, "Color for the debug inline value text.")));
registerColor("editor.inlineValuesBackground", "#ffc80033", ( localize(8324, "Color for the debug inline value background.")));
class InlineSegment {
    constructor(column, text) {
        this.column = column;
        this.text = text;
    }
}
function formatHoverContent(contentText) {
    if (contentText.includes(",") && contentText.includes("=")) {
        const customSplit = text => {
            const splits = [];
            let equalsFound = 0;
            let start = 0;
            for (let i = 0; i < text.length; i++) {
                if (text[i] === "=") {
                    if (equalsFound === 0) {
                        equalsFound++;
                        continue;
                    }
                    const commaIndex = text.lastIndexOf(",", i);
                    if (commaIndex !== -1 && commaIndex >= start) {
                        splits.push(commaIndex);
                        start = commaIndex + 1;
                    }
                    equalsFound++;
                }
            }
            const result = [];
            let s = 0;
            for (const index of splits) {
                result.push(text.substring(s, index).trim());
                s = index + 1;
            }
            if (s < text.length) {
                result.push(text.substring(s).trim());
            }
            return result;
        };
        const pairs = customSplit(contentText);
        const formattedPairs = ( pairs.map(pair => {
            const equalsIndex = pair.indexOf("=");
            if (equalsIndex !== -1) {
                const indent = " ".repeat(equalsIndex + 2);
                const [firstLine, ...restLines] = pair.split(/\r?\n/);
                return [firstLine, ...( restLines.map(line => indent + line))].join("\n");
            }
            return pair;
        }));
        return ( new MarkdownString()).appendCodeblock("", formattedPairs.join(",\n"));
    }
    return ( new MarkdownString()).appendCodeblock("", contentText);
}
function createInlineValueDecoration(
    lineNumber,
    contentText,
    classNamePrefix,
    column = Constants.MAX_SAFE_SMALL_INTEGER,
    viewportMaxCol = MAX_INLINE_DECORATOR_LENGTH
) {
    const rawText = contentText;
    if (contentText.length > viewportMaxCol) {
        contentText = contentText.substring(0, viewportMaxCol) + "...";
    }
    return [{
        range: {
            startLineNumber: lineNumber,
            endLineNumber: lineNumber,
            startColumn: column,
            endColumn: column
        },
        options: {
            description: `${classNamePrefix}-inline-value-decoration-spacer`,
            after: {
                content: noBreakWhitespace,
                cursorStops: InjectedTextCursorStops.None
            },
            showIfCollapsed: true
        }
    }, {
        range: {
            startLineNumber: lineNumber,
            endLineNumber: lineNumber,
            startColumn: column,
            endColumn: column
        },
        options: {
            description: `${classNamePrefix}-inline-value-decoration`,
            after: {
                content: replaceWsWithNoBreakWs(contentText),
                inlineClassName: `${classNamePrefix}-inline-value`,
                inlineClassNameAffectsLetterSpacing: true,
                cursorStops: InjectedTextCursorStops.None
            },
            showIfCollapsed: true,
            hoverMessage: formatHoverContent(rawText)
        }
    }];
}
function replaceWsWithNoBreakWs(str) {
    return str.replace(/[ \t\n]/g, noBreakWhitespace);
}
function createInlineValueDecorationsInsideRange(expressions, ranges, model, wordToLineNumbersMap) {
    const nameValueMap = ( new Map());
    for (const expr of expressions) {
        nameValueMap.set(expr.name, expr.value);
        if (nameValueMap.size >= MAX_NUM_INLINE_VALUES) {
            break;
        }
    }
    const lineToNamesMap = ( new Map());
    nameValueMap.forEach((_value, name) => {
        const lineNumbers = wordToLineNumbersMap.get(name);
        if (lineNumbers) {
            for (const lineNumber of lineNumbers) {
                if (( ranges.some(r => lineNumber >= r.startLineNumber && lineNumber <= r.endLineNumber))) {
                    if (!( lineToNamesMap.has(lineNumber))) {
                        lineToNamesMap.set(lineNumber, []);
                    }
                    if (lineToNamesMap.get(lineNumber).indexOf(name) === -1) {
                        lineToNamesMap.get(lineNumber).push(name);
                    }
                }
            }
        }
    });
    return ( [...lineToNamesMap].map(([line, names]) => ({
        line,
        variables: ( names.sort((first, second) => {
            const content = model.getLineContent(line);
            return content.indexOf(first) - content.indexOf(second);
        }).map(name => ({
            name,
            value: nameValueMap.get(name)
        })))
    })));
}
function getWordToLineNumbersMap(model, lineNumber, result) {
    const lineLength = model.getLineLength(lineNumber);
    if (lineLength > MAX_TOKENIZATION_LINE_LEN) {
        return;
    }
    const lineContent = model.getLineContent(lineNumber);
    model.tokenization.forceTokenization(lineNumber);
    const lineTokens = model.tokenization.getLineTokens(lineNumber);
    for (let tokenIndex = 0, tokenCount = lineTokens.getCount(); tokenIndex < tokenCount; tokenIndex++) {
        const tokenType = lineTokens.getStandardTokenType(tokenIndex);
        if (tokenType === StandardTokenType.Other) {
            DEFAULT_WORD_REGEXP.lastIndex = 0;
            const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
            const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
            const tokenStr = lineContent.substring(tokenStartOffset, tokenEndOffset);
            const wordMatch = DEFAULT_WORD_REGEXP.exec(tokenStr);
            if (wordMatch) {
                const word = wordMatch[0];
                if (!( result.has(word))) {
                    result.set(word, []);
                }
                result.get(word).push(lineNumber);
            }
        }
    }
}
let DebugEditorContribution = class DebugEditorContribution {
    constructor(
        editor,
        debugService,
        instantiationService,
        commandService,
        configurationService,
        hostService,
        uriIdentityService,
        contextKeyService,
        languageFeaturesService,
        featureDebounceService,
        editorService
    ) {
        this.editor = editor;
        this.debugService = debugService;
        this.instantiationService = instantiationService;
        this.commandService = commandService;
        this.configurationService = configurationService;
        this.hostService = hostService;
        this.uriIdentityService = uriIdentityService;
        this.languageFeaturesService = languageFeaturesService;
        this.editorService = editorService;
        this.mouseDown = false;
        this.gutterIsHovered = false;
        this.altListener = ( new MutableDisposable());
        this.altPressed = false;
        this.displayedStore = ( new DisposableStore());
        this.allowScrollToExceptionWidget = true;
        this.shouldScrollToExceptionWidget = () => this.allowScrollToExceptionWidget;
        this.defaultHoverLockout = ( new MutableDisposable());
        this.oldDecorations = this.editor.createDecorationsCollection();
        this.debounceInfo = featureDebounceService.for(languageFeaturesService.inlineValuesProvider, "InlineValues", {
            min: DEAFULT_INLINE_DEBOUNCE_DELAY
        });
        this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor);
        this.toDispose = [this.defaultHoverLockout, this.altListener, this.displayedStore];
        this.registerListeners();
        this.exceptionWidgetVisible = CONTEXT_EXCEPTION_WIDGET_VISIBLE.bindTo(contextKeyService);
        this.toggleExceptionWidget();
    }
    registerListeners() {
        this.toDispose.push(
            this.debugService.getViewModel().onDidFocusStackFrame(e => this.onFocusStackFrame(e.stackFrame))
        );
        this.toDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
        this.toDispose.push(this.editor.onMouseUp(() => this.mouseDown = false));
        this.toDispose.push(this.editor.onMouseMove(e => this.onEditorMouseMove(e)));
        this.toDispose.push(this.editor.onMouseLeave(e => {
            const hoverDomNode = this.hoverWidget.getDomNode();
            if (!hoverDomNode) {
                return;
            }
            const rect = hoverDomNode.getBoundingClientRect();
            if (e.event.posx < rect.left || e.event.posx > rect.right || e.event.posy < rect.top || e.event.posy > rect.bottom) {
                this.hideHoverWidget();
            }
        }));
        this.toDispose.push(this.editor.onKeyDown(e => this.onKeyDown(e)));
        this.toDispose.push(this.editor.onDidChangeModelContent(() => {
            this._wordToLineNumbersMap = undefined;
            this.updateInlineValuesScheduler.schedule();
        }));
        this.toDispose.push(
            this.debugService.getViewModel().onWillUpdateViews(() => this.updateInlineValuesScheduler.schedule())
        );
        this.toDispose.push(
            this.debugService.getViewModel().onDidEvaluateLazyExpression(() => this.updateInlineValuesScheduler.schedule())
        );
        this.toDispose.push(this.editor.onDidChangeModel(async () => {
            this.addDocumentListeners();
            this.toggleExceptionWidget();
            this.hideHoverWidget();
            this._wordToLineNumbersMap = undefined;
            const stackFrame = this.debugService.getViewModel().focusedStackFrame;
            await this.updateInlineValueDecorations(stackFrame);
        }));
        this.toDispose.push(this.editor.onDidScrollChange(() => {
            this.hideHoverWidget();
            const model = this.editor.getModel();
            if (model && ( this.languageFeaturesService.inlineValuesProvider.has(model))) {
                this.updateInlineValuesScheduler.schedule();
            }
        }));
        this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => {
            if (e.affectsConfiguration("editor.hover")) {
                this.updateHoverConfiguration();
            }
        }));
        this.toDispose.push(this.debugService.onDidChangeState(state => {
            if (state !== State.Stopped) {
                this.toggleExceptionWidget();
            }
        }));
        this.updateHoverConfiguration();
    }
    updateHoverConfiguration() {
        const model = this.editor.getModel();
        if (model) {
            this.editorHoverOptions = this.configurationService.getValue("editor.hover", {
                resource: model.uri,
                overrideIdentifier: model.getLanguageId()
            });
        }
    }
    addDocumentListeners() {
        const stackFrame = this.debugService.getViewModel().focusedStackFrame;
        const model = this.editor.getModel();
        if (model) {
            this.applyDocumentListeners(model, stackFrame);
        }
    }
    applyDocumentListeners(model, stackFrame) {
        if (!stackFrame || !this.uriIdentityService.extUri.isEqual(model.uri, stackFrame.source.uri)) {
            this.altListener.clear();
            return;
        }
        const ownerDocument = this.editor.getContainerDomNode().ownerDocument;
        this.altListener.value = addDisposableListener(ownerDocument, "keydown", keydownEvent => {
            const standardKeyboardEvent = ( new StandardKeyboardEvent(keydownEvent));
            if (standardKeyboardEvent.keyCode === KeyCode.Alt) {
                this.altPressed = true;
                const debugHoverWasVisible = this.hoverWidget.isVisible();
                this.hoverWidget.hide();
                this.defaultHoverLockout.clear();
                if (debugHoverWasVisible && this.hoverPosition) {
                    this.showEditorHover(this.hoverPosition.position, false);
                }
                const onKeyUp = ( new DomEmitter(ownerDocument, "keyup"));
                const listener = Event.any(this.hostService.onDidChangeFocus, onKeyUp.event)(keyupEvent => {
                    let standardKeyboardEvent = undefined;
                    if (isKeyboardEvent(keyupEvent)) {
                        standardKeyboardEvent = ( new StandardKeyboardEvent(keyupEvent));
                    }
                    if (!standardKeyboardEvent || standardKeyboardEvent.keyCode === KeyCode.Alt) {
                        this.altPressed = false;
                        this.preventDefaultEditorHover();
                        listener.dispose();
                        onKeyUp.dispose();
                    }
                });
            }
        });
    }
    async showHover(position, focus, mouseEvent) {
        this.preventDefaultEditorHover();
        const sf = this.debugService.getViewModel().focusedStackFrame;
        const model = this.editor.getModel();
        if (sf && model && this.uriIdentityService.extUri.isEqual(sf.source.uri, model.uri)) {
            const result = await this.hoverWidget.showAt(position, focus, mouseEvent);
            if (result === ShowDebugHoverResult.NOT_AVAILABLE) {
                this.showEditorHover(position, focus);
            }
        } else {
            this.showEditorHover(position, focus);
        }
    }
    preventDefaultEditorHover() {
        if (this.defaultHoverLockout.value || this.editorHoverOptions?.enabled === "off") {
            return;
        }
        const hoverController = this.editor.getContribution(ContentHoverController.ID);
        hoverController?.hideContentHover();
        this.editor.updateOptions({
            hover: {
                enabled: "off"
            }
        });
        this.defaultHoverLockout.value = {
            dispose: () => {
                this.editor.updateOptions({
                    hover: {
                        enabled: this.editorHoverOptions?.enabled ?? "on"
                    }
                });
            }
        };
    }
    showEditorHover(position, focus) {
        const hoverController = this.editor.getContribution(ContentHoverController.ID);
        const range = ( new Range(position.lineNumber, position.column, position.lineNumber, position.column));
        this.defaultHoverLockout.clear();
        hoverController?.showContentHover(range, HoverStartMode.Immediate, HoverStartSource.Mouse, focus);
    }
    async onFocusStackFrame(sf) {
        const model = this.editor.getModel();
        if (model) {
            this.applyDocumentListeners(model, sf);
            if (sf && this.uriIdentityService.extUri.isEqual(sf.source.uri, model.uri)) {
                await this.toggleExceptionWidget();
            } else {
                this.hideHoverWidget();
            }
        }
        await this.updateInlineValueDecorations(sf);
    }
    get hoverDelay() {
        const baseDelay = this.editorHoverOptions?.delay || 0;
        const delayFactor = clamp(2 - (baseDelay - 300) / 600, 1, 2);
        return baseDelay * delayFactor;
    }
    get showHoverScheduler() {
        const scheduler = ( new RunOnceScheduler(() => {
            if (this.hoverPosition && !this.altPressed) {
                this.showHover(this.hoverPosition.position, false, this.hoverPosition.event);
            }
        }, this.hoverDelay));
        this.toDispose.push(scheduler);
        return scheduler;
    }
    hideHoverWidget() {
        if (this.hoverWidget.willBeVisible()) {
            this.hoverWidget.hide();
        }
        this.showHoverScheduler.cancel();
        this.defaultHoverLockout.clear();
    }
    onEditorMouseDown(mouseEvent) {
        this.mouseDown = true;
        if (mouseEvent.target.type === MouseTargetType.CONTENT_WIDGET && mouseEvent.target.detail === DebugHoverWidget.ID) {
            return;
        }
        this.hideHoverWidget();
    }
    onEditorMouseMove(mouseEvent) {
        if (this.debugService.state !== State.Stopped) {
            return;
        }
        const target = mouseEvent.target;
        const stopKey = isMacintosh ? "metaKey" : "ctrlKey";
        if (!this.altPressed) {
            if (target.type === MouseTargetType.GUTTER_GLYPH_MARGIN) {
                this.defaultHoverLockout.clear();
                this.gutterIsHovered = true;
            } else if (this.gutterIsHovered) {
                this.gutterIsHovered = false;
                this.updateHoverConfiguration();
            }
        }
        if ((target.type === MouseTargetType.CONTENT_WIDGET && target.detail === DebugHoverWidget.ID) || this.hoverWidget.isInSafeTriangle(mouseEvent.event.posx, mouseEvent.event.posy)) {
            const sticky = this.editorHoverOptions?.sticky ?? true;
            if (sticky || this.hoverWidget.isShowingComplexValue || mouseEvent.event[stopKey]) {
                return;
            }
        }
        if (target.type === MouseTargetType.CONTENT_TEXT) {
            if (target.position && !Position.equals(target.position, this.hoverPosition?.position || null) && !this.hoverWidget.isInSafeTriangle(mouseEvent.event.posx, mouseEvent.event.posy)) {
                this.hoverPosition = {
                    position: target.position,
                    event: mouseEvent.event
                };
                this.preventDefaultEditorHover();
                this.showHoverScheduler.schedule(this.hoverDelay);
            }
        } else if (!this.mouseDown) {
            this.hideHoverWidget();
        }
    }
    onKeyDown(e) {
        const stopKey = isMacintosh ? KeyCode.Meta : KeyCode.Ctrl;
        if (e.keyCode !== stopKey && e.keyCode !== KeyCode.Alt) {
            this.hideHoverWidget();
        }
    }
    async toggleExceptionWidget() {
        const model = this.editor.getModel();
        const focusedSf = this.debugService.getViewModel().focusedStackFrame;
        const callStack = focusedSf ? focusedSf.thread.getCallStack() : null;
        if (!model || !focusedSf || !callStack || callStack.length === 0) {
            this.closeExceptionWidget();
            return;
        }
        const exceptionSf = callStack.find(
            sf => !!(sf && sf.source && sf.source.available && sf.source.presentationHint !== "deemphasize")
        );
        if (!exceptionSf || exceptionSf !== focusedSf) {
            this.closeExceptionWidget();
            return;
        }
        const sameUri = this.uriIdentityService.extUri.isEqual(exceptionSf.source.uri, model.uri);
        if (this.exceptionWidget && !sameUri) {
            this.closeExceptionWidget();
        } else if (sameUri) {
            const activeControl = this.editorService.activeTextEditorControl;
            const isActiveEditor = activeControl === this.editor;
            const exceptionInfo = await focusedSf.thread.exceptionInfo;
            if (exceptionInfo) {
                if (isActiveEditor) {
                    this.showExceptionWidget(
                        exceptionInfo,
                        this.debugService.getViewModel().focusedSession,
                        exceptionSf.range.startLineNumber,
                        exceptionSf.range.startColumn
                    );
                } else {
                    this.showExceptionWidgetWithoutScroll(
                        exceptionInfo,
                        this.debugService.getViewModel().focusedSession,
                        exceptionSf.range.startLineNumber,
                        exceptionSf.range.startColumn
                    );
                }
            }
        }
    }
    showExceptionWidget(exceptionInfo, debugSession, lineNumber, column) {
        if (this.exceptionWidget) {
            this.exceptionWidget.dispose();
        }
        this.exceptionWidget = this.instantiationService.createInstance(
            ExceptionWidget,
            this.editor,
            exceptionInfo,
            debugSession,
            this.shouldScrollToExceptionWidget
        );
        this.exceptionWidget.show({
            lineNumber,
            column
        }, 0);
        this.exceptionWidget.focus();
        this.editor.revealRangeInCenter({
            startLineNumber: lineNumber,
            startColumn: column,
            endLineNumber: lineNumber,
            endColumn: column
        });
        this.exceptionWidgetVisible.set(true);
    }
    showExceptionWidgetWithoutScroll(exceptionInfo, debugSession, lineNumber, column) {
        if (this.exceptionWidget) {
            this.exceptionWidget.dispose();
        }
        this.allowScrollToExceptionWidget = false;
        const currentScrollTop = this.editor.getScrollTop();
        const visibleRanges = this.editor.getVisibleRanges();
        if (visibleRanges.length === 0) {
            this.exceptionWidget = this.instantiationService.createInstance(
                ExceptionWidget,
                this.editor,
                exceptionInfo,
                debugSession,
                this.shouldScrollToExceptionWidget
            );
            this.exceptionWidget.show({
                lineNumber,
                column
            }, 0);
            this.exceptionWidgetVisible.set(true);
            this.allowScrollToExceptionWidget = true;
            return;
        }
        const firstVisibleLine = visibleRanges[0].startLineNumber;
        this.exceptionWidget = this.instantiationService.createInstance(
            ExceptionWidget,
            this.editor,
            exceptionInfo,
            debugSession,
            this.shouldScrollToExceptionWidget
        );
        this.exceptionWidget.show({
            lineNumber,
            column
        }, 0);
        this.exceptionWidgetVisible.set(true);
        if (lineNumber < firstVisibleLine) {
            const scrollAdjustment = this.exceptionWidget.getWhitespaceHeight();
            this.editor.setScrollTop(currentScrollTop + scrollAdjustment, ScrollType.Immediate);
        }
        this.allowScrollToExceptionWidget = true;
    }
    closeExceptionWidget() {
        if (this.exceptionWidget) {
            const shouldFocusEditor = this.exceptionWidget.hasFocus();
            this.exceptionWidget.dispose();
            this.exceptionWidget = undefined;
            this.exceptionWidgetVisible.set(false);
            if (shouldFocusEditor) {
                this.editor.focus();
            }
        }
    }
    async addLaunchConfiguration() {
        const model = this.editor.getModel();
        if (!model) {
            return;
        }
        let configurationsArrayPosition;
        let lastProperty;
        const getConfigurationPosition = () => {
            let depthInArray = 0;
            visit(model.getValue(), {
                onObjectProperty: property => {
                    lastProperty = property;
                },
                onArrayBegin: offset => {
                    if (lastProperty === "configurations" && depthInArray === 0) {
                        configurationsArrayPosition = model.getPositionAt(offset + 1);
                    }
                    depthInArray++;
                },
                onArrayEnd: () => {
                    depthInArray--;
                }
            });
        };
        getConfigurationPosition();
        if (!configurationsArrayPosition) {
            const {
                tabSize,
                insertSpaces
            } = model.getOptions();
            const eol = model.getEOL();
            const edit = (basename(model.uri.fsPath) === "launch.json") ? setProperty(model.getValue(), ["configurations"], [], {
                tabSize,
                insertSpaces,
                eol
            })[0] : setProperty(model.getValue(), ["launch"], {
                "configurations": []
            }, {
                tabSize,
                insertSpaces,
                eol
            })[0];
            const startPosition = model.getPositionAt(edit.offset);
            const lineNumber = startPosition.lineNumber;
            const range = ( new Range(
                lineNumber,
                startPosition.column,
                lineNumber,
                model.getLineMaxColumn(lineNumber)
            ));
            model.pushEditOperations(null, [EditOperation.replace(range, edit.content)], () => null);
            getConfigurationPosition();
        }
        if (!configurationsArrayPosition) {
            return;
        }
        this.editor.focus();
        const insertLine = position => {
            if (model.getLineLastNonWhitespaceColumn(position.lineNumber) > position.column) {
                this.editor.setPosition(position);
                this.instantiationService.invokeFunction(accessor => {
                    CoreEditingCommands.LineBreakInsert.runEditorCommand(accessor, this.editor, null);
                });
            }
            this.editor.setPosition(position);
            return this.commandService.executeCommand(InsertLineAfterAction.ID);
        };
        await insertLine(configurationsArrayPosition);
        await this.commandService.executeCommand("editor.action.triggerSuggest");
    }
    get removeInlineValuesScheduler() {
        return ( new RunOnceScheduler(() => {
            this.displayedStore.clear();
            this.oldDecorations.clear();
        }, 100));
    }
    get updateInlineValuesScheduler() {
        const model = this.editor.getModel();
        return ( new RunOnceScheduler(
            async () => await this.updateInlineValueDecorations(this.debugService.getViewModel().focusedStackFrame),
            model ? this.debounceInfo.get(model) : DEAFULT_INLINE_DEBOUNCE_DELAY
        ));
    }
    async updateInlineValueDecorations(stackFrame) {
        const var_value_format = "{0} = {1}";
        const separator = ", ";
        const model = this.editor.getModel();
        const inlineValuesSetting = this.configurationService.getValue("debug").inlineValues;
        const inlineValuesTurnedOn = inlineValuesSetting === true || inlineValuesSetting === "on" || (inlineValuesSetting === "auto" && model && ( this.languageFeaturesService.inlineValuesProvider.has(model)));
        if (!inlineValuesTurnedOn || !model || !stackFrame || ( model.uri.toString()) !== ( stackFrame.source.uri.toString())) {
            if (!this.removeInlineValuesScheduler.isScheduled()) {
                this.removeInlineValuesScheduler.schedule();
            }
            return;
        }
        this.removeInlineValuesScheduler.cancel();
        this.displayedStore.clear();
        const viewRanges = this.editor.getVisibleRangesPlusViewportAboveBelow();
        let allDecorations;
        const cts = ( new CancellationTokenSource());
        this.displayedStore.add(toDisposable(() => cts.dispose(true)));
        if (( this.languageFeaturesService.inlineValuesProvider.has(model))) {
            const findVariable = async (_key, caseSensitiveLookup) => {
                const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range);
                const key = caseSensitiveLookup ? _key : _key.toLowerCase();
                for (const scope of scopes) {
                    const variables = await scope.getChildren();
                    const found = variables.find(
                        v => caseSensitiveLookup ? (v.name === key) : (v.name.toLowerCase() === key)
                    );
                    if (found) {
                        return found.value;
                    }
                }
                return undefined;
            };
            const ctx = {
                frameId: stackFrame.frameId,
                stoppedLocation: ( new Range(
                    stackFrame.range.startLineNumber,
                    stackFrame.range.startColumn + 1,
                    stackFrame.range.endLineNumber,
                    stackFrame.range.endColumn + 1
                ))
            };
            const providers = this.languageFeaturesService.inlineValuesProvider.ordered(model).reverse();
            allDecorations = [];
            const lineDecorations = ( new Map());
            const promises = providers.flatMap(provider => ( viewRanges.map(
                range => Promise.resolve(provider.provideInlineValues(model, range, ctx, cts.token)).then(async result => {
                    if (result) {
                        for (const iv of result) {
                            let text = undefined;
                            switch (iv.type) {
                            case "text":
                                text = iv.text;
                                break;
                            case "variable":
                                {
                                    let va = iv.variableName;
                                    if (!va) {
                                        const lineContent = model.getLineContent(iv.range.startLineNumber);
                                        va = lineContent.substring(iv.range.startColumn - 1, iv.range.endColumn - 1);
                                    }
                                    const value = await findVariable(va, iv.caseSensitiveLookup);
                                    if (value) {
                                        text = format(var_value_format, va, value);
                                    }
                                    break;
                                }
                            case "expression":
                                {
                                    let expr = iv.expression;
                                    if (!expr) {
                                        const lineContent = model.getLineContent(iv.range.startLineNumber);
                                        expr = lineContent.substring(iv.range.startColumn - 1, iv.range.endColumn - 1);
                                    }
                                    if (expr) {
                                        const expression = ( new Expression(expr));
                                        await expression.evaluate(stackFrame.thread.session, stackFrame, "watch", true);
                                        if (expression.available) {
                                            text = format(var_value_format, expr, expression.value);
                                        }
                                    }
                                    break;
                                }
                            }
                            if (text) {
                                const line = iv.range.startLineNumber;
                                let lineSegments = lineDecorations.get(line);
                                if (!lineSegments) {
                                    lineSegments = [];
                                    lineDecorations.set(line, lineSegments);
                                }
                                if (!( lineSegments.some(iv => iv.text === text))) {
                                    lineSegments.push(( new InlineSegment(iv.range.startColumn, text)));
                                }
                            }
                        }
                    }
                }, err => {
                    onUnexpectedExternalError(err);
                })
            )));
            const startTime = Date.now();
            await Promise.all(promises);
            this.updateInlineValuesScheduler.delay = this.debounceInfo.update(model, Date.now() - startTime);
            lineDecorations.forEach((segments, line) => {
                if (segments.length > 0) {
                    segments = segments.sort((a, b) => a.column - b.column);
                    const text = ( segments.map(s => s.text)).join(separator);
                    const editorWidth = this.editor.getLayoutInfo().width;
                    const fontInfo = this.editor.getOption(EditorOption.fontInfo);
                    const viewportMaxCol = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth);
                    allDecorations.push(
                        ...createInlineValueDecoration(line, text, "debug", undefined, viewportMaxCol)
                    );
                }
            });
        } else {
            const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range);
            const scopesWithVariables = await Promise.all(( scopes.map(async scope => ({
                scope,
                variables: await scope.getChildren()
            }))));
            const valuesPerLine = ( new Map());
            for (const {
                scope,
                variables
            } of scopesWithVariables) {
                let scopeRange = ( new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn));
                if (scope.range) {
                    scopeRange = scopeRange.setStartPosition(scope.range.startLineNumber, scope.range.startColumn);
                }
                const ownRanges = ( viewRanges.map(r => r.intersectRanges(scopeRange))).filter(isDefined);
                this._wordToLineNumbersMap ??= ( new WordsToLineNumbersCache(model));
                for (const range of ownRanges) {
                    this._wordToLineNumbersMap.ensureRangePopulated(range);
                }
                const mapped = createInlineValueDecorationsInsideRange(variables, ownRanges, model, this._wordToLineNumbersMap.value);
                for (const {
                    line,
                    variables
                } of mapped) {
                    let values = valuesPerLine.get(line);
                    if (!values) {
                        values = ( new Map());
                        valuesPerLine.set(line, values);
                    }
                    for (const {
                        name,
                        value
                    } of variables) {
                        if (!( values.has(name))) {
                            values.set(name, value);
                        }
                    }
                }
            }
            allDecorations = [...valuesPerLine.entries()].flatMap(([line, values]) => {
                const text = ( [...values].map(([n, v]) => `${n} = ${v}`)).join(", ");
                const editorWidth = this.editor.getLayoutInfo().width;
                const fontInfo = this.editor.getOption(EditorOption.fontInfo);
                const viewportMaxCol = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth);
                return createInlineValueDecoration(line, text, "debug", undefined, viewportMaxCol);
            });
        }
        if (cts.token.isCancellationRequested) {
            return;
        }
        let preservePosition;
        if (this.editor.getOption(EditorOption.wordWrap) !== "off") {
            const position = this.editor.getPosition();
            if (position && ( this.editor.getVisibleRanges().some(r => r.containsPosition(position)))) {
                preservePosition = {
                    position,
                    top: this.editor.getTopForPosition(position.lineNumber, position.column)
                };
            }
        }
        this.oldDecorations.set(allDecorations);
        if (preservePosition) {
            const top = this.editor.getTopForPosition(preservePosition.position.lineNumber, preservePosition.position.column);
            this.editor.setScrollTop(
                this.editor.getScrollTop() - (preservePosition.top - top),
                ScrollType.Immediate
            );
        }
    }
    dispose() {
        this.hoverWidget?.dispose();
        this.configurationWidget?.dispose();
        this.exceptionWidget?.dispose();
        this.toDispose = dispose(this.toDispose);
    }
};
DebugEditorContribution.__decorator = ( __decorate([memoize], DebugEditorContribution.prototype, "showHoverScheduler", null));
DebugEditorContribution.__decorator = ( __decorate(
    [memoize],
    DebugEditorContribution.prototype,
    "removeInlineValuesScheduler",
    null
));
DebugEditorContribution.__decorator = ( __decorate(
    [memoize],
    DebugEditorContribution.prototype,
    "updateInlineValuesScheduler",
    null
));
DebugEditorContribution = ( __decorate([( __param(1, IDebugService)), ( __param(2, IInstantiationService)), ( __param(3, ICommandService)), ( __param(4, IConfigurationService)), ( __param(5, IHostService)), ( __param(6, IUriIdentityService)), ( __param(7, IContextKeyService)), ( __param(8, ILanguageFeaturesService)), ( __param(9, ILanguageFeatureDebounceService)), ( __param(10, IEditorService))], DebugEditorContribution));
class WordsToLineNumbersCache {
    constructor(model) {
        this.model = model;
        this.value = ( new Map());
        this.intervals = ( new Uint8Array(Math.ceil(model.getLineCount() / 8)));
    }
    ensureRangePopulated(range) {
        for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) {
            const bin = lineNumber >> 3;
            const bit = 1 << (lineNumber & 0b111);
            if (!(this.intervals[bin] & bit)) {
                getWordToLineNumbersMap(this.model, lineNumber, this.value);
                this.intervals[bin] |= bit;
            }
        }
    }
}
CommandsRegistry.registerCommand("_executeInlineValueProvider", async (accessor, uri, iRange, context) => {
    assertType(URI.isUri(uri));
    assertType(Range.isIRange(iRange));
    if (!context || typeof context.frameId !== "number" || !Range.isIRange(context.stoppedLocation)) {
        throw illegalArgument("context");
    }
    const model = accessor.get(IModelService).getModel(uri);
    if (!model) {
        throw illegalArgument("uri");
    }
    const range = Range.lift(iRange);
    const {
        inlineValuesProvider
    } = accessor.get(ILanguageFeaturesService);
    const providers = inlineValuesProvider.ordered(model);
    const providerResults = await Promise.all(( providers.map(
        provider => provider.provideInlineValues(model, range, context, CancellationToken.None)
    )));
    return providerResults.flat().filter(isDefined);
});

export { DebugEditorContribution, createInlineValueDecoration, formatHoverContent };
