var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _PlayEditor_state, _PlayEditor_dispatchThrottledSourceChangedEvent;
import { autocompletion, completeFromList } from '@codemirror/autocomplete';
import { tsxLanguage } from '@codemirror/lang-javascript';
import { linter } from '@codemirror/lint';
import { EditorState } from '@codemirror/state';
import { hoverTooltip } from '@codemirror/view';
import * as tsvfs from '@typescript/vfs';
import { EditorView, basicSetup } from 'codemirror';
import { LitElement, css, html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import ts, { displayPartsToString } from 'typescript';
import { appEntrypointFilename } from '../../bundler/compiler.js';
import { throttle } from '../../utils/throttle.js';
import { Bubble } from '../bubble.js';
let PlayEditor = class PlayEditor extends LitElement {
    constructor() {
        super(...arguments);
        _PlayEditor_state.set(this, void 0);
        _PlayEditor_dispatchThrottledSourceChangedEvent.set(this, throttle((code) => {
            this.dispatchEvent(Bubble('edit', code));
        }, 500));
    }
    render() {
        return html `<div class="editor"></div>`;
    }
    firstUpdated() {
        const src = this.env.getSourceFile(appEntrypointFilename)?.text ?? '';
        __classPrivateFieldSet(this, _PlayEditor_state, EditorState.create({
            extensions: [
                basicSetup,
                tsxLanguage,
                linter(async (_view) => {
                    const diagnostics = this.env.languageService.getSemanticDiagnostics(appEntrypointFilename);
                    return diagnostics.map(tsDiagnosticToMirrorDiagnostic);
                }, { delay: 100 }),
                tsAutocomplete(this.env),
                tsHoverTip(this.env)
            ],
            doc: src
        }), "f");
        const editor = new EditorView({
            state: __classPrivateFieldGet(this, _PlayEditor_state, "f"),
            parent: this._root,
            dispatch: async (transaction) => {
                editor.update([transaction]);
                if (transaction.docChanged) {
                    const code = transaction.state.doc.sliceString(0);
                    __classPrivateFieldGet(this, _PlayEditor_dispatchThrottledSourceChangedEvent, "f").call(this, code || ' '); // empty strings trigger file deletion!
                }
            }
        });
    }
};
_PlayEditor_state = new WeakMap();
_PlayEditor_dispatchThrottledSourceChangedEvent = new WeakMap();
PlayEditor.styles = css `
    .editor {
      background: #f8f9fa;
      display: contents;
    }
    .cm-editor {
      min-height: 0;
      height: 100%;
    }
  `;
__decorate([
    property({ attribute: false })
], PlayEditor.prototype, "env", void 0);
__decorate([
    query('div')
], PlayEditor.prototype, "_root", void 0);
PlayEditor = __decorate([
    customElement('play-editor')
], PlayEditor);
export { PlayEditor };
function tsDiagnosticToMirrorDiagnostic(diagnostic) {
    const severity = {
        [ts.DiagnosticCategory.Error]: 'error',
        [ts.DiagnosticCategory.Warning]: 'warning',
        [ts.DiagnosticCategory.Suggestion]: 'info',
        [ts.DiagnosticCategory.Message]: 'info'
    }[diagnostic.category];
    return {
        from: diagnostic.start ?? 0,
        to: (diagnostic.start ?? 0) + (diagnostic.length ?? 0),
        severity,
        message: String(diagnostic.messageText),
        source: diagnostic.source ?? ''
    };
}
function tsHoverTip(env) {
    return hoverTooltip((_editor, pos) => {
        // to-do: clean up.
        console.log('hover');
        const info = env.languageService.getQuickInfoAtPosition(appEntrypointFilename, pos);
        if (!info)
            return null;
        console.log('hover with info', info);
        return {
            pos: info.textSpan.start,
            end: info.textSpan.start + info.textSpan.length,
            create() {
                const dom = document.createElement('div');
                dom.className = 'cm-quickinfo-tooltip';
                dom.textContent =
                    displayPartsToString(info.displayParts) +
                        (info.documentation?.length
                            ? '\n' + displayPartsToString(info.documentation)
                            : '');
                return { dom };
            }
        };
    });
}
function tsAutocomplete(env) {
    return autocompletion({
        override: [
            ctx => {
                const completions = env.languageService.getCompletionsAtPosition(appEntrypointFilename, ctx.pos, {
                // to-do: explore the many completion options here.
                });
                if (!completions)
                    return null;
                return completeFromList(completions.entries.map(completion => ({
                    type: completion.kind,
                    label: completion.name,
                    detail: completion.labelDetails?.detail ?? ''
                })))(ctx);
            }
        ]
    });
}
