/**
* Local Monaco setup — bundles monaco-editor with the app so no CDN is
* needed and the editor works fully offline.
*
* Must be imported once, before any @monaco-editor/react mounts.
*/
import * as monaco from "monaco-editor";
import { loader } from "@monaco-editor/react";
import EditorWorker from "monaco-editor/editor/editor.worker?worker";
import { useDbViewerStore } from "../stores/dbViewerStore";
import { useUiStore } from "../stores/uiStore";
import {
buildSqlSuggestions,
buildColumnSuggestions,
getColumnsForTable,
getCachedColumns,
parseTableRef,
} from "./sqlCompletion";
// Use the bundled editor worker (SQL has no dedicated language worker)
self.MonacoEnvironment = {
getWorker: () => new EditorWorker(),
};
// Hand the already-imported monaco instance to @monaco-editor/react so it
// skips its CDN download entirely.
loader.config({ monaco });
// SQL autocomplete:
// - after `table.` (or `schema.table.`): suggest that table's columns
// (fetched lazily via schema introspection and cached per schema)
// - otherwise: keywords + table names from the active schema
monaco.languages.registerCompletionItemProvider("sql", {
provideCompletionItems: (
model,
position,
): monaco.languages.CompletionList | Promise => {
const { tables, currentSchema } = useDbViewerStore.getState();
const line = model.getLineContent(position.lineNumber);
const before = line.slice(0, position.column - 1);
const word = model.getWordUntilPosition(position);
const range = new monaco.Range(
position.lineNumber,
word.startColumn,
position.lineNumber,
word.endColumn,
);
const withRange = (s: { label: string; insertText: string; kind: string }) => ({
label: s.label,
insertText: s.insertText,
kind:
s.kind === "keyword"
? monaco.languages.CompletionItemKind.Keyword
: s.kind === "table"
? monaco.languages.CompletionItemKind.Struct
: monaco.languages.CompletionItemKind.Field,
range,
});
const tableRef = parseTableRef(before);
if (tableRef) {
const schema = tableRef.schema ?? currentSchema;
const connectionId = useUiStore.getState().activeConnectionId;
const cached = schema ? getCachedColumns(schema, tableRef.table) : undefined;
if (cached) {
return {
suggestions: buildColumnSuggestions(cached).map(withRange),
};
}
// Not introspected yet: warm the cache in the background and ask Monaco
// to re-request once the columns are available.
void getColumnsForTable(connectionId, schema, tableRef.table);
return { suggestions: [], incomplete: true };
}
return {
suggestions: buildSqlSuggestions(tables, currentSchema).map(withRange),
};
},
});