import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { useRef, useEffect, useState, useMemo } from 'react';
import { editor as monacoEditorAPI, languages as monacoLanguagesAPI, } from 'monaco-editor';
import { observer } from 'mobx-react-lite';
import { clsx, FilledWindowMaximizeIcon, LongArrowAltDownIcon, LongArrowAltUpIcon, Dialog, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalFooterButton, } from '@finos/legend-art';
import { getBaseCodeEditorOptions, getCodeEditorValue, normalizeLineEnding, clearMarkers, setErrorMarkers, CODE_EDITOR_LANGUAGE, CODE_EDITOR_THEME, PURE_CODE_EDITOR_WORD_SEPARATORS, } from '@finos/legend-code-editor';
import { disposeCodeEditor } from '@finos/legend-lego/code-editor';
import { debounce, guaranteeNonNullable, noop, removePrefix, } from '@finos/legend-shared';
import { flowResult } from 'mobx';
import { ParserError } from '@finos/legend-graph';
import { DEFAULT_TAB_SIZE, useApplicationStore, } from '@finos/legend-application';
import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
const LambdaEditor_ErrorFeedback = (props) => {
    const { error, discardChanges } = props;
    if (!error) {
        return null;
    }
    return (_jsxs("div", { className: "lambda-editor__error-feedback", children: [_jsx("div", { className: "lambda-editor__error-feedback__error__message", children: error.message }), error instanceof ParserError && (_jsx("div", { className: "lambda-editor__error-feedback__parsing-error__content", children: _jsx("button", { className: "lambda-editor__error-feedback__parsing-error__discard-changes-btn", onClick: discardChanges, tabIndex: -1, children: "Discard Changes" }) }))] }));
};
export async function getCodeSuggestions(position, model, lambdaEditorState) {
    const textUntilPosition = model.getValueInRange({
        startLineNumber: 1,
        startColumn: 1,
        endLineNumber: position.lineNumber,
        endColumn: position.column,
    });
    const currentWord = model.getWordAtPosition(position);
    let suggestions = [];
    const SPECIAL_CHAR = '>';
    try {
        suggestions = (await lambdaEditorState.getCodeComplete(textUntilPosition))
            .completions;
        suggestions.forEach((suggestion) => {
            if (textUntilPosition.length) {
                // HACK for special char '>' behaving weirdly
                const lastCharInContext = textUntilPosition.slice(-1);
                const firstCharInSuggestion = suggestion.completion[0];
                if (lastCharInContext === SPECIAL_CHAR &&
                    firstCharInSuggestion === SPECIAL_CHAR) {
                    {
                        suggestion.completion = removePrefix(suggestion.completion, guaranteeNonNullable(textUntilPosition.slice(-1)));
                    }
                }
            }
        });
    }
    catch {
        // do nothing: provide no suggestions when error ocurred
    }
    return suggestions.map((suggestion) => ({
        label: suggestion.display,
        kind: monacoLanguagesAPI.CompletionItemKind.Text, // TODO?: the engine should provide this information
        filterText: suggestion.completion,
        insertTextRules: monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
        insertText: suggestion.completion,
        // NOTE: the following replace range is equivalent to the current word range, if there is a current word,
        // replace this word with the suggestions, otherwise, if there is no current word, we use the current position
        // as insertion point for the suggestion. This is due to the fact that the auto-completer gives full word
        // suggestions even when the user is typing the middle of that word
        //
        // For example, if the current input is 'a' and the suggestion is 'abc', we need to make sure
        // the suggestion is inserted so that the final result is 'abc', not 'aabc'
        range: {
            startLineNumber: position.lineNumber,
            startColumn: currentWord?.startColumn ?? position.column,
            endLineNumber: position.lineNumber,
            endColumn: currentWord?.endColumn ?? position.column,
        },
        command: {
            id: 'editor.action.triggerSuggest',
        },
    }));
}
const LambdaEditor_Inner = observer((props) => {
    const { className, disabled, lambdaEditorState, transformStringToLambda, expectedType, onExpectedTypeLabelSelect, matchedExpectedType, forceBackdrop, disableExpansion, forceExpansion, disablePopUp, inline, hideErrorBar, autoFocus, openInPopUp, onEditorFocus, onEditorBlur, } = props;
    const applicationStore = useApplicationStore();
    const onDidChangeModelContentEventDisposer = useRef(undefined);
    const onDidFocusEditorWidgetDisposer = useRef(undefined);
    const onDidBlurEditorTextDisposer = useRef(undefined);
    const onDidFocusEditorTextDisposer = useRef(undefined);
    const suggestionsProvider = useRef(undefined);
    const value = normalizeLineEnding(lambdaEditorState.lambdaString);
    const parserError = lambdaEditorState.parserError;
    const compilationError = lambdaEditorState.compilationError;
    const selectTypeLabel = () => onExpectedTypeLabelSelect?.();
    const [isExpanded, setExpanded] = useState(Boolean(forceExpansion));
    const [editor, setEditor] = useState();
    const textInputRef = useRef(null);
    const transformLambdaToString = async (pretty) => {
        transformStringToLambda?.cancel();
        return flowResult(lambdaEditorState.convertLambdaObjectToGrammarString({
            pretty: pretty,
        })).catch(applicationStore.alertUnhandledError);
    };
    const discardChanges = applicationStore.guardUnhandledError(() => transformLambdaToString(isExpanded));
    const toggleExpandedMode = () => {
        if (!forceExpansion && !parserError) {
            transformLambdaToString(!isExpanded).catch(applicationStore.alertUnhandledError);
            setExpanded(!isExpanded);
        }
        else if (!forceExpansion && parserError) {
            setExpanded(!isExpanded);
        }
    };
    useEffect(() => {
        if (!editor && textInputRef.current) {
            const element = textInputRef.current;
            const lambdaEditorOptions = inline
                ? {
                    renderLineHighlight: 'none',
                    lineHeight: 24,
                    overviewRulerBorder: false, // hide overview ruler (no current way to hide this completely yet)
                    overviewRulerLanes: 0,
                    hideCursorInOverviewRuler: false,
                    glyphMargin: false,
                    folding: false,
                    minimap: { enabled: false },
                    lineNumbers: 'off',
                    lineNumbersMinChars: 0,
                    lineDecorationsWidth: 5,
                    snippetSuggestions: 'none',
                    scrollbar: { vertical: 'hidden' },
                }
                : {
                    padding: { top: 20, bottom: 20 },
                };
            const _editor = monacoEditorAPI.create(element, {
                ...getBaseCodeEditorOptions(),
                language: CODE_EDITOR_LANGUAGE.PURE,
                theme: applicationStore.layoutService
                    .TEMPORARY__isLightColorThemeEnabled
                    ? CODE_EDITOR_THEME.BUILT_IN__VSCODE_LIGHT
                    : CODE_EDITOR_THEME.DEFAULT_DARK,
                ...lambdaEditorOptions,
            });
            setEditor(_editor);
        }
    }, [editor, applicationStore, inline]);
    // set styling for expanded mode
    useEffect(() => {
        if (editor) {
            const currentClassName = editor.getRawOptions().extraEditorClassName;
            const isInExpanded = currentClassName === 'lambda-editor__expanded';
            if (isInExpanded !== isExpanded) {
                editor.updateOptions(isExpanded
                    ? {
                        extraEditorClassName: 'lambda-editor__editor__input__expanded',
                    }
                    : {
                        extraEditorClassName: 'lambda-editor__editor__input__compressed',
                    });
                // set the value here so we don't lose the error when toggling between expand/collape modes
                const currentValue = getCodeEditorValue(editor);
                editor.setValue(currentValue);
            }
        }
    }, [editor, isExpanded]);
    // set styling when theme changes
    useEffect(() => {
        if (editor) {
            editor.updateOptions({
                theme: applicationStore.layoutService
                    .TEMPORARY__isLightColorThemeEnabled
                    ? CODE_EDITOR_THEME.BUILT_IN__VSCODE_LIGHT
                    : CODE_EDITOR_THEME.DEFAULT_DARK,
            });
        }
    }, [
        editor,
        applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled,
    ]);
    // set backdrop to force user to fix parser error when it happens
    useEffect(() => {
        if (parserError) {
            applicationStore.layoutService.setShowBackdrop(true);
        }
        else if (!forceBackdrop) {
            // make sure the backdrop is no longer `needed` for blocking by another parser error before hiding it
            // NOTE: this has a serious drawback, see the documentation for `forceBackdrop` prop of `LambdaEditor`
            // for better context
            applicationStore.layoutService.setShowBackdrop(false);
        }
    }, [applicationStore, parserError, forceBackdrop]);
    if (editor) {
        /**
         * See the extensive note about this instantiation in `LambdaEditor`. The fact that `transformStringToLambda` can change
         * since it does not solely depends on `LambdaEditorState` but also the `disabled` flag means that the update function
         * can go stale, so we cannot place this `onDidChangeModelContent` in a one-time called instantiation of the editor
         * (i.e. the first `useEffect` where we create the editor). As such, we have to use refs and disposer to update this every time
         * the lambda editor is re-rendered.
         *
         * A potential bug that could come up if we place this logic in the `useEffect` for instantiating the editor is:
         * 1. Initially set the `disabled` to true, then switch it back to `false` (using a timer or something)
         * 2. Type something in the lambda editor, the transform function is `undefined` and does not update the underlying lambda
         */
        onDidChangeModelContentEventDisposer.current?.dispose();
        onDidChangeModelContentEventDisposer.current =
            editor.onDidChangeModelContent((event) => {
                const currentVal = getCodeEditorValue(editor);
                /**
                 * Avoid unecessary setting of lambda string. Also, this prevents clearing the non-parser error on first render.
                 * Since this method is guaranteed to be called one time during the first rendering when we first set the
                 * value for the lambda editor, we do not want to clear any existing non-parser error in case it is set by methods
                 * like reveal error in each editor
                 */
                if (currentVal !== value) {
                    lambdaEditorState.setLambdaString(currentVal);
                    /**
                     * Here we clear the error as user changes the input
                     * NOTE: we don't reset the parser error here, we could, but with that, we have to assume that the parsing check is
                     * pretty quick--almost near real time, but if after typing new character, we clear the parsing error and the user
                     * still make mistake, then the warning message will appear to flash, which is bad UX, so for now, we leave it be
                     */
                    lambdaEditorState.setCompilationError(undefined);
                }
                /**
                 * This method MUST run on the first rendering of the lambda editor, as it will update the lambda object. This is
                 * needed for new lambda where a lot of time is just a stub lambda. Without having this method called on the first
                 * rendering, that stub lambda will remain stub lambda until user starts typing something in the lambda editor.
                 * This stub lambda sometimes does not even get registered in change detection and causing the user to lose data
                 * Although, technically a stub lambda is useless, so this is not too serious, but it may come across as buggy
                 */
                transformStringToLambda?.cancel();
                if (transformStringToLambda) {
                    const stringToLambdaTransformation = transformStringToLambda();
                    if (stringToLambdaTransformation) {
                        flowResult(stringToLambdaTransformation).catch(applicationStore.alertUnhandledError);
                    }
                }
            });
        onDidFocusEditorWidgetDisposer.current?.dispose();
        onDidFocusEditorWidgetDisposer.current = editor.onDidFocusEditorWidget(() => {
            onEditorFocus?.();
        });
        onDidBlurEditorTextDisposer.current = editor.onDidBlurEditorText(() => {
            if (onEditorBlur) {
                transformStringToLambda?.cancel();
                onEditorBlur();
            }
            if (lambdaEditorState.typeAheadEnabled) {
                suggestionsProvider.current?.dispose();
                suggestionsProvider.current = undefined;
            }
        });
        if (lambdaEditorState.typeAheadEnabled) {
            onDidFocusEditorTextDisposer.current = editor.onDidFocusEditorText(() => {
                suggestionsProvider.current?.dispose();
                suggestionsProvider.current =
                    monacoLanguagesAPI.registerCompletionItemProvider(CODE_EDITOR_LANGUAGE.PURE, {
                        // NOTE: this is a hack to fetch suggestions from engine for every keystroke
                        triggerCharacters: [...PURE_CODE_EDITOR_WORD_SEPARATORS, '$'],
                        provideCompletionItems: async (model, position) => {
                            const suggestions = await getCodeSuggestions(position, model, lambdaEditorState);
                            return { suggestions };
                        },
                    });
            });
        }
        // Set the text value
        const currentValue = getCodeEditorValue(editor);
        const editorModel = editor.getModel();
        const currentConfig = editor.getRawOptions();
        if (currentValue !== value) {
            editor.setValue(value);
        }
        if (currentConfig.readOnly !== disabled) {
            editor.updateOptions({
                readOnly: disabled,
            });
        }
        // Set the errors
        if (editorModel) {
            editorModel.updateOptions({ tabSize: DEFAULT_TAB_SIZE });
            const error = parserError ?? compilationError;
            if (error?.sourceInformation) {
                setErrorMarkers(editorModel, [
                    {
                        message: error.message,
                        startLineNumber: error.sourceInformation.startLine,
                        startColumn: error.sourceInformation.startColumn,
                        endLineNumber: error.sourceInformation.endLine,
                        endColumn: error.sourceInformation.endColumn,
                    },
                ]);
            }
            else {
                clearMarkers();
            }
        }
    }
    // auto-focus
    useEffect(() => {
        if (editor && autoFocus) {
            editor.focus();
        }
    }, [autoFocus, editor]);
    // dispose editor
    useEffect(() => () => {
        if (editor) {
            disposeCodeEditor(editor);
            onDidChangeModelContentEventDisposer.current?.dispose();
            onDidFocusEditorWidgetDisposer.current?.dispose();
            onDidFocusEditorTextDisposer.current?.dispose();
            onDidBlurEditorTextDisposer.current?.dispose();
            suggestionsProvider.current?.dispose();
        }
    }, [editor]);
    return (_jsxs(_Fragment, { children: [_jsxs("div", { className: clsx('lambda-editor', className, {
                    'lambda-editor__expanded': isExpanded,
                }), children: [_jsx("div", { "data-testid": QUERY_BUILDER_TEST_ID.LAMBDA_EDITOR__EDITOR_INPUT, className: "lambda-editor__editor__input", children: _jsx("div", { className: "code-editor__body", ref: textInputRef }) }), Boolean(expectedType) && (_jsxs("div", { className: "lambda-editor__editor__info", children: [onExpectedTypeLabelSelect && (_jsx("button", { className: clsx('lambda-editor__editor__expected-return-type lambda-editor__editor__expected-return-type--clickable', {
                                    'lambda-editor__editor__expected-return-type--highlighted': matchedExpectedType?.(),
                                }), onClick: selectTypeLabel, tabIndex: -1, title: "Toggle highlight expected type", children: expectedType?.name ?? 'unknown' })), !onExpectedTypeLabelSelect && (_jsx("div", { className: clsx('lambda-editor__editor__expected-return-type', {
                                    'lambda-editor__editor__expected-return-type--highlighted': matchedExpectedType?.(),
                                }), children: expectedType?.name ?? 'unknown' }))] })), !disableExpansion && !forceExpansion && (_jsx("button", { className: "lambda-editor__editor__expand-btn", onClick: toggleExpandedMode, tabIndex: -1, title: "Toggle Expand", children: isExpanded ? _jsx(LongArrowAltUpIcon, {}) : _jsx(LongArrowAltDownIcon, {}) })), !disablePopUp && (_jsx("button", { className: "lambda-editor__action", onClick: openInPopUp, tabIndex: -1, title: "Open in a popup...", children: _jsx(FilledWindowMaximizeIcon, {}) }))] }), !hideErrorBar && (_jsx(LambdaEditor_ErrorFeedback, { error: parserError ?? compilationError, discardChanges: discardChanges }))] }));
});
const LambdaEditor_PopUp = observer((props) => {
    const { className, disabled, lambdaEditorState, transformStringToLambda, title, onClose, } = props;
    const applicationStore = useApplicationStore();
    const onDidChangeModelContentEventDisposer = useRef(undefined);
    const value = normalizeLineEnding(lambdaEditorState.lambdaString);
    const parserError = lambdaEditorState.parserError;
    const compilationError = lambdaEditorState.compilationError;
    const [editor, setEditor] = useState();
    const textInputRef = useRef(null);
    const transformLambdaToString = async (pretty) => {
        transformStringToLambda?.cancel();
        return flowResult(lambdaEditorState.convertLambdaObjectToGrammarString({
            pretty: pretty,
        })).catch(applicationStore.alertUnhandledError);
    };
    const discardChanges = applicationStore.guardUnhandledError(() => transformLambdaToString(true));
    const onEnter = () => {
        if (!editor && textInputRef.current) {
            const element = textInputRef.current;
            const _editor = monacoEditorAPI.create(element, {
                ...getBaseCodeEditorOptions(),
                language: CODE_EDITOR_LANGUAGE.PURE,
                theme: applicationStore.layoutService
                    .TEMPORARY__isLightColorThemeEnabled
                    ? CODE_EDITOR_THEME.BUILT_IN__VSCODE_LIGHT
                    : CODE_EDITOR_THEME.DEFAULT_DARK,
            });
            setEditor(_editor);
        }
    };
    if (editor) {
        /**
         * See the extensive note about this instantiation in `LambdaEditor`. The fact that `transformStringToLambda` can change
         * since it does not solely depends on `LambdaEditorState` but also the `disabled` flag means that the update function
         * can go stale, so we cannot place this `onDidChangeModelContent` in a one-time called instantiation of the editor
         * (i.e. the first `useEffect` where we create the editor). As such, we have to use refs and disposer to update this every time
         * the lambda editor is re-rendered.
         *
         * A potential bug that could come up if we place this logic in the `useEffect` for instantiating the editor is:
         * 1. Initially set the `disabled` to true, then switch it back to `false` (using a timer or something)
         * 2. Type something in the lambda editor, the transform function is `undefined` and does not update the underlying lambda
         */
        onDidChangeModelContentEventDisposer.current?.dispose();
        onDidChangeModelContentEventDisposer.current =
            editor.onDidChangeModelContent((event) => {
                const currentVal = getCodeEditorValue(editor);
                /**
                 * Avoid unecessary setting of lambda string. Also, this prevents clearing the non-parser error on first render.
                 * Since this method is guaranteed to be called one time during the first rendering when we first set the
                 * value for the lambda editor, we do not want to clear any existing non-parser error in case it is set by methods
                 * like reveal error in each editor
                 */
                if (currentVal !== value) {
                    lambdaEditorState.setLambdaString(currentVal);
                    /**
                     * Here we clear the error as user changes the input
                     * NOTE: we don't reset the parser error here, we could, but with that, we have to assume that the parsing check is
                     * pretty quick--almost near real time, but if after typing new character, we clear the parsing error and the user
                     * still make mistake, then the warning message will appear to flash, which is bad UX, so for now, we leave it be
                     */
                    lambdaEditorState.setCompilationError(undefined);
                }
                /**
                 * This method MUST run on the first rendering of the lambda editor, as it will update the lambda object. This is
                 * needed for new lambda where a lot of time is just a stub lambda. Without having this method called on the first
                 * rendering, that stub lambda will remain stub lambda until user starts typing something in the lambda editor.
                 * This stub lambda sometimes does not even get registered in change detection and causing the user to lose data
                 * Although, technically a stub lambda is useless, so this is not too serious, but it may come across as buggy
                 */
                transformStringToLambda?.cancel();
                if (transformStringToLambda) {
                    const stringToLambdaTransformation = transformStringToLambda();
                    if (stringToLambdaTransformation) {
                        flowResult(stringToLambdaTransformation).catch(applicationStore.alertUnhandledError);
                    }
                }
            });
        // Set the text value
        const currentValue = getCodeEditorValue(editor);
        const editorModel = editor.getModel();
        const currentConfig = editor.getRawOptions();
        if (currentValue !== value) {
            editor.setValue(value);
        }
        if (currentConfig.readOnly !== disabled) {
            editor.updateOptions({
                readOnly: disabled,
            });
        }
        // Set the errors
        if (editorModel) {
            editorModel.updateOptions({ tabSize: DEFAULT_TAB_SIZE });
            const error = parserError ?? compilationError;
            if (error?.sourceInformation) {
                setErrorMarkers(editorModel, [
                    {
                        message: error.message,
                        startLineNumber: error.sourceInformation.startLine,
                        startColumn: error.sourceInformation.startColumn,
                        endLineNumber: error.sourceInformation.endLine,
                        endColumn: error.sourceInformation.endColumn,
                    },
                ]);
            }
            else {
                clearMarkers();
            }
        }
    }
    useEffect(() => {
        if (!lambdaEditorState.parserError) {
            flowResult(lambdaEditorState.convertLambdaObjectToGrammarString({
                pretty: true,
                preserveCompilationError: true,
            })).catch(applicationStore.alertUnhandledError);
        }
    }, [applicationStore, lambdaEditorState]);
    // dispose editor
    useEffect(() => () => {
        if (editor) {
            disposeCodeEditor(editor);
            onDidChangeModelContentEventDisposer.current?.dispose();
        }
    }, [editor]);
    return (_jsx(Dialog, { open: true, slotProps: {
            transition: {
                onEnter,
            },
        }, onClose: noop, classes: {
            root: 'editor-modal__root-container',
            container: 'editor-modal__container',
            paper: 'editor-modal__content',
        }, children: _jsxs(Modal, { darkMode: !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled, className: clsx('editor-modal lambda-editor__popup__modal', {
                'lambda-editor__popup__modal--has-error': Boolean(lambdaEditorState.parserError),
            }, {
                'lambda-editor--light': applicationStore.layoutService
                    .TEMPORARY__isLightColorThemeEnabled,
            }), children: [_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { title: title ?? 'Edit Lambda' }), lambdaEditorState.parserError && (_jsx("div", { className: "modal__title__error-badge", children: "Failed to parse lambda" }))] }), _jsx(ModalBody, { children: _jsx("div", { className: clsx('lambda-editor__popup__content', className), children: _jsx("div", { "data-testid": QUERY_BUILDER_TEST_ID.LAMBDA_EDITOR__EDITOR_INPUT, className: "lambda-editor__editor__input", children: _jsx("div", { className: "code-editor__body", ref: textInputRef }) }) }) }), _jsxs(ModalFooter, { children: [_jsx(ModalFooterButton, { text: "Proceed", onClick: onClose, disabled: Boolean(lambdaEditorState.parserError) }), _jsx(ModalFooterButton, { className: "btn--caution", text: "Cancel", onClick: discardChanges, type: "secondary" })] })] }) }));
});
export const InlineLambdaEditor = observer((props) => {
    const { className, lambdaEditorState, disabled, forceBackdrop, expectedType, onExpectedTypeLabelSelect, matchedExpectedType, disableExpansion, forceExpansion, disablePopUp, hideErrorBar, autoFocus, onEditorFocus, onEditorBlur, } = props;
    const [showPopUp, setShowPopUp] = useState(false);
    const openInPopUp = () => setShowPopUp(true);
    const closePopUp = () => setShowPopUp(false);
    const debouncedTransformStringToLambda = useMemo(() => disabled
        ? null
        : debounce(() => lambdaEditorState.convertLambdaGrammarStringToObject(), 1000), [lambdaEditorState, disabled]);
    if (!disablePopUp && showPopUp) {
        return (_jsxs(_Fragment, { children: [_jsx("div", { className: "lambda-editor" }), _jsx(LambdaEditor_PopUp, { className: className, disabled: disabled, lambdaEditorState: lambdaEditorState, transformStringToLambda: debouncedTransformStringToLambda, onClose: closePopUp })] }));
    }
    return (_jsx(LambdaEditor_Inner
    /**
     * See the usage of `transformStringToLambda` as well as the instatiation of the editor in `LambdaEditorInner`.
     * One of the big problem is that the editor uses lambda editor state (there are some non-trivial logic there, that
     * handles string <-> lambda object conversion), but there are certain operations in this app that can potentially
     * remove and recreate the lambda editor state, such as global generate or global compile, for such case, `LambdaEditorInner`
     * receives a new lambda editor state, but since React is smart about redrawing the DOM, it will not recreate the instance
     * of `monaco-editor` (see the useEffect() block), as such, the editor is using a stale state. That is definitely a bug;
     * and to demonstrate, we can try the following sequence of actions:
     *
     * 1. Type something that parses in the lambda editor
     * 2. Wait till the parse call finishes, hit F10 (trigger state update)
     * 3. Type something to cause parser error
     * 4. We will see that nothing happens. If we inspect network call, we will see a call returns with parsing error, but
     *    the editor is refering to the old state, hence no reporting
     *
     * As such, the most appropriate resolution is to intentionally force remount of lambda editor when user perform a state
     * refresh operations. To do this, we use React `key` field with UUID bound to the state, as the state is recreated, the UUID
     * changes and the editor is recreated. However, this alone is not enough because we use `useMemo` for the debounced
     * transform function, which relies on another value other than the state (i.e. `disabled` flag), the same problem happens
     * as the function goes stale if `disabled` flag changes value. For that, see the implementation trick below (involving refs and disposer)
     *
     * So technically, we don't need to do the force-remount using `key`, but to be cleaner, we prevent other poential bugs, we do it anyway
     * to reset everything.
     */
    , { className: className, inline: true, disabled: disabled, lambdaEditorState: lambdaEditorState, transformStringToLambda: debouncedTransformStringToLambda, expectedType: expectedType, matchedExpectedType: matchedExpectedType, onExpectedTypeLabelSelect: onExpectedTypeLabelSelect, forceBackdrop: forceBackdrop, disableExpansion: disableExpansion, disablePopUp: disablePopUp, autoFocus: autoFocus, openInPopUp: openInPopUp, onEditorFocus: onEditorFocus, onEditorBlur: onEditorBlur, hideErrorBar: hideErrorBar, forceExpansion: disableExpansion !== undefined
            ? !disableExpansion && forceExpansion
            : forceExpansion }, lambdaEditorState.uuid));
});
export const LambdaEditor = observer((props) => {
    const { className, lambdaEditorState, disabled, forceBackdrop, autoFocus, onEditorFocus, hideErrorBar, } = props;
    const debouncedTransformStringToLambda = useMemo(() => disabled
        ? null
        : debounce(() => lambdaEditorState.convertLambdaGrammarStringToObject(), 1000), [lambdaEditorState, disabled]);
    return (_jsx(LambdaEditor_Inner, { className: className, disabled: disabled, lambdaEditorState: lambdaEditorState, transformStringToLambda: debouncedTransformStringToLambda, forceBackdrop: forceBackdrop, autoFocus: autoFocus, onEditorFocus: onEditorFocus, disableExpansion: true, forceExpansion: true, disablePopUp: true, hideErrorBar: hideErrorBar }, lambdaEditorState.uuid));
});
//# sourceMappingURL=LambdaEditor.js.map