import * as monaco from 'monaco-editor'; import type { editor } from 'monaco-editor'; import { locate } from '../parser/locate'; import { offsetToPosition } from '../parser/lexer'; import { buildCompletions } from './completions'; import type { MutableRefObject } from 'react'; export interface ProviderOptions { fetchFieldNamesRef: MutableRefObject<((nestedPath?: string) => Promise) | undefined>; fetchFieldValuesRef: MutableRefObject<((fieldName: string) => Promise) | undefined>; variablesRef: MutableRefObject; editorRef: MutableRefObject; } export function getKQLCompletionProvider(opts: ProviderOptions): monaco.languages.CompletionItemProvider { return { triggerCharacters: [':', '(', '{', ' ', '"', '$', '*'], provideCompletionItems: async (model, position) => { // Only serve requests from this editor's own model if (opts.editorRef.current?.getModel()?.id !== model.id) { return { suggestions: [] }; } const value = model.getValue(); const offset = model.getOffsetAt(position); // locate() only needs the token stream — a full parse() here would be wasted work per keystroke const situation = locate(value, offset); const startPos = offsetToPosition(value, situation.replaceRange.start); const range = { startLineNumber: startPos.lineNumber, startColumn: startPos.column, endLineNumber: position.lineNumber, endColumn: position.column, }; const suggestions = await buildCompletions({ situation, range, variables: opts.variablesRef.current, fetchFieldNames: opts.fetchFieldNamesRef.current, fetchFieldValues: opts.fetchFieldValuesRef.current, }); return { suggestions }; }, }; }