import { div } from "@bokehjs/core/dom";
import { HTMLBox, HTMLBoxView } from "./layout";
import { ID } from "./util";
export class AcePlotView extends HTMLBoxView {
    static __name__ = "AcePlotView";
    _editor;
    _langTools;
    _modelist;
    _container;
    connect_signals() {
        super.connect_signals();
        const { code, theme, language, filename, print_margin, annotations, soft_tabs, indent, readonly } = this.model.properties;
        this.on_change(code, () => this._update_code_from_model());
        this.on_change(theme, () => this._update_theme());
        this.on_change(language, () => this._update_language());
        this.on_change(filename, () => this._update_filename());
        this.on_change(print_margin, () => this._update_print_margin());
        this.on_change(annotations, () => this._add_annotations());
        this.on_change(indent, () => this._editor.setOptions({ tabSize: this.model.indent }));
        this.on_change(soft_tabs, () => this._editor.setOptions({ useSoftTabs: this.model.soft_tabs }));
        this.on_change(readonly, () => {
            this._editor.setReadOnly(this.model.readonly);
        });
    }
    render() {
        super.render();
        this._container = div({
            id: ID(),
            style: {
                width: "100%",
                height: "100%",
                zIndex: "0",
            },
        });
        this.shadow_el.append(this._container);
        this._container.textContent = this.model.code;
        this._editor = ace.edit(this._container);
        this._editor.renderer.attachToShadowRoot();
        this._langTools = ace.require("ace/ext/language_tools");
        this._modelist = ace.require("ace/ext/modelist");
        this._editor.setOptions({
            enableBasicAutocompletion: true,
            tabSize: this.model.indent,
            useSoftTabs: this.model.soft_tabs,
            enableSnippets: true,
            fontFamily: "monospace", //hack for cursor position
        });
        this._update_theme();
        this._update_filename();
        this._update_language();
        this._editor.setReadOnly(this.model.readonly);
        this._editor.setShowPrintMargin(this.model.print_margin);
        // if on keyup, update code from editor
        if (this.model.on_keyup) {
            this._editor.on("change", () => this._update_code_from_editor());
        }
        else {
            this._editor.on("blur", () => this._update_code_from_editor());
            this._editor.commands.addCommand({
                name: "updateCodeFromEditor",
                bindKey: { win: "Ctrl-Enter", mac: "Command-Enter" },
                exec: () => {
                    this._update_code_from_editor();
                },
            });
        }
        this._editor.on("change", () => this._update_code_input_from_editor());
    }
    _update_code_from_model() {
        if (this._editor && this._editor.getValue() != this.model.code) {
            // Save the current cursor position
            const cursorPosition = this._editor.getCursorPosition();
            const scrollTop = this._editor.session.getScrollTop();
            const scrollLeft = this._editor.session.getScrollLeft();
            // Update the value without selecting all text
            // The second parameter (-1) keeps cursor where it is
            this._editor.setValue(this.model.code, -1);
            // Restore cursor position only if it's valid in the new content
            const newRowCount = this._editor.session.getLength();
            if (cursorPosition.row < newRowCount) {
                const newRowLength = this._editor.session.getLine(cursorPosition.row).length;
                const newColumn = Math.min(cursorPosition.column, newRowLength);
                this._editor.moveCursorToPosition({ row: cursorPosition.row, column: newColumn });
            }
            else {
                // If the cursor was beyond the new document length, move to end
                this._editor.moveCursorToPosition({ row: newRowCount - 1, column: this._editor.session.getLine(newRowCount - 1).length });
            }
            this._editor.clearSelection();
            this._editor.session.setScrollTop(scrollTop);
            this._editor.session.setScrollLeft(scrollLeft);
        }
    }
    _update_print_margin() {
        this._editor.setShowPrintMargin(this.model.print_margin);
    }
    _update_code_from_editor() {
        if (this._editor.getValue() != this.model.code) {
            this.model.code = this._editor.getValue();
        }
    }
    _update_code_input_from_editor() {
        if (this._editor.getValue() != this.model.code_input) {
            this.model.code_input = this._editor.getValue();
        }
    }
    _update_theme() {
        if (this._editor) {
            this._editor.setTheme(`ace/theme/${this.model.theme}`);
        }
    }
    _update_filename() {
        if (this.model.filename) {
            const mode = this._modelist.getModeForPath(this.model.filename).mode;
            this.model.language = mode.slice(9);
        }
    }
    _update_language() {
        if (this.model.language != null) {
            this._editor.session.setMode(`ace/mode/${this.model.language}`);
            // Re-apply annotations after mode change, since setMode may
            // spawn a new worker that would overwrite user annotations.
            this._add_annotations();
        }
    }
    _add_annotations() {
        // Toggle the Ace worker BEFORE setting annotations, because
        // disabling the worker clears the session's annotations.
        this._editor.session.setUseWorker(this.model.annotations.length === 0);
        this._editor.session.setAnnotations(this.model.annotations);
    }
    after_layout() {
        super.after_layout();
        if (this._editor !== undefined) {
            this._editor.resize();
        }
    }
}
export class AcePlot extends HTMLBox {
    static __name__ = "AcePlot";
    constructor(attrs) {
        super(attrs);
    }
    static __module__ = "panel.models.ace";
    static {
        this.prototype.default_view = AcePlotView;
        this.define(({ Any, Bool, Int, List, Str, Nullable }) => ({
            annotations: [List(Any), []],
            code: [Str, ""],
            code_input: [Str, ""],
            filename: [Nullable(Str), null],
            indent: [Int, 4],
            language: [Str, ""],
            on_keyup: [Bool, true],
            print_margin: [Bool, false],
            theme: [Str, "github_light_default"],
            readonly: [Bool, false],
            soft_tabs: [Bool, false],
        }));
        this.override({
            height: 300,
            width: 300,
        });
    }
}
//# sourceMappingURL=ace.js.map