import type { VendorConfig } from '../types'; /** * Build a Monarch tokens provider tuned to the supplied vendor. * * The grammar has two top-level states: `searchRoot` (the `key:value` segment) * and `analyzeRoot` (the SQL-flavoured portion after the first top-level `|`). * State transitions are driven exclusively by an unbracketed pipe — strings, * parens and brackets nest into dedicated states so their inner `|` is never * mistaken for a segment separator. */ export function buildMonarchLanguage(vendor: VendorConfig): any { return { defaultToken: '', tokenPostfix: '.logql-' + vendor.id, ignoreCase: true, searchKeywords: vendor.searchKeywords.map((k) => k.toLowerCase()), sqlKeywords: vendor.sqlKeywords.map((k) => k.toLowerCase()), builtinFunctions: vendor.sqlFunctions.map((f) => f.name.toLowerCase()), builtinFields: (vendor.searchBuiltinFields ?? []).map((f) => f.name), brackets: [ { open: '(', close: ')', token: 'delimiter.parenthesis' }, { open: '{', close: '}', token: 'delimiter.curly' }, { open: '[', close: ']', token: 'delimiter.square' }, ], escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4})/, tokenizer: { root: [{ include: '@searchRoot' }], // ----- Search segment ----- searchRoot: [ { include: '@whitespace' }, { include: '@variable' }, // key followed by `:` or `=` -> field highlight [/[a-zA-Z_][\w.\-]*(?=\s*[:=])/, 'type.identifier'], // builtin special fields like __topic__, _exists_ [/_[a-zA-Z_][\w]*_/, 'type.identifier'], // logical keywords (case-insensitive) [/[a-zA-Z_]\w*/, { cases: { '@searchKeywords': 'keyword', '@default': 'identifier' } }], // strings [/"/, { token: 'string.quote', next: '@string_double' }], [/'/, { token: 'string.quote', next: '@string_single' }], [/`/, { token: 'string.quote', next: '@string_backtick' }], // numbers [/-?\d+(\.\d+)?([eE][+-]?\d+)?/, 'number'], // operators [/(>=|<=|!=|<>|[:=<>])/, 'operator'], // wildcards [/[*?]/, 'keyword.operator'], // delimiters [/[(),]/, '@brackets'], [/\[/, { token: '@brackets', next: '@searchBracket' }], // pipe transitions to analyze segment [/\|/, { token: 'operator.pipe', next: '@analyzeRoot' }], ], searchBracket: [ { include: '@whitespace' }, { include: '@variable' }, [/[a-zA-Z_][\w.\-]*/, { cases: { '@searchKeywords': 'keyword', '@default': 'identifier' } }], [/-?\d+(\.\d+)?/, 'number'], [/\]/, { token: '@brackets', next: '@pop' }], ], // ----- Analyze (SQL-ish) segment ----- analyzeRoot: [ { include: '@whitespace' }, { include: '@comments' }, { include: '@variable' }, // function call: identifier followed by ( [ /[a-zA-Z_][\w]*(?=\s*\()/, { cases: { '@builtinFunctions': 'keyword.function', '@default': 'identifier.function' } }, ], [ /[a-zA-Z_][\w]*/, { cases: { '@sqlKeywords': 'keyword', '@default': 'identifier' } }, ], [/"/, { token: 'string.quote', next: '@string_double' }], [/'/, { token: 'string.quote', next: '@string_single' }], [/`/, { token: 'string.quote', next: '@string_backtick' }], [/-?\d+(\.\d+)?([eE][+-]?\d+)?/, 'number'], [/(>=|<=|!=|<>|[<>=])/, 'operator'], [/\|\|/, 'operator'], [/[+\-*/%]/, 'operator'], [/[,.;]/, 'delimiter'], [/[(){}\[\]]/, '@brackets'], ], // ----- Strings (shared between segments) ----- string_double: [ { include: '@variable' }, [/[^"\\$]+/, 'string'], [/@escapes/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/\$/, 'string'], [/"/, { token: 'string.quote', next: '@pop' }], ], string_single: [ { include: '@variable' }, [/[^'\\$]+/, 'string'], [/@escapes/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/\$/, 'string'], [/'/, { token: 'string.quote', next: '@pop' }], ], string_backtick: [ { include: '@variable' }, [/[^`$]+/, 'string'], [/\$/, 'string'], [/`/, { token: 'string.quote', next: '@pop' }], ], // ----- Comments (only in analyze segment) ----- comments: [ [/--+.*/, 'comment'], [/\/\*/, { token: 'comment.quote', next: '@blockComment' }], ], blockComment: [ [/[^*/]+/, 'comment'], [/\*\//, { token: 'comment.quote', next: '@pop' }], [/./, 'comment'], ], // ----- Shared sub-states ----- whitespace: [[/\s+/, 'white']], variable: [ [/\$\{[A-Za-z_][\w\-.]*\}/, 'variable'], [/\$/, 'variable'], ], }, }; } export function languageIdFor(vendor: VendorConfig['id']): string { return 'logql-' + vendor; }