// ─── Token types ────────────────────────────────────────────────── export enum TokenKind { LCurly, // { RCurly, // } Pipe, // | PipeEquals, // |= PipeRegex, // |~ Equals, // = NotEquals, // != RegexEquals, // =~ RegexNotEquals, // !~ GreaterThan, // > GreaterEquals, // >= LessThan, // < LessEquals, // <= String, // "...", '...', `...` Number, // 123, 45.6 Duration, // 5m, 1h30m Bytes, // 42MB Ident, // identifier / keyword Comma, // , LParen, // ( RParen, // ) LBracket, // [ RBracket, // ] Comment, // #... Whitespace, EOF, Error, } export interface Token { kind: TokenKind; value: string; start: number; end: number; } // ─── AST types ──────────────────────────────────────────────────── export interface LokiQuery { streamSelector: StreamSelector | null; pipeline: PipelineStage[]; metricExpr: MetricExpr | null; errors: ParseError[]; } export interface StreamSelector { matchers: Matcher[]; } export interface Matcher { label: string; operator: string; // = != =~ !~ value: string; } export type PipelineStage = LineFilter | JsonParser | LogfmtParser | RegexpParser | LabelFilter | UnrecognizedStage; export interface LineFilter { type: 'lineFilter'; operator: string; // |= != |~ !~ value: string; } export interface JsonParser { type: 'jsonParser'; expressions?: string[]; } export interface LogfmtParser { type: 'logfmtParser'; expressions?: string[]; } export interface RegexpParser { type: 'regexpParser'; pattern: string; } export interface LabelFilter { type: 'labelFilter'; conditions: LabelFilterCondition[]; } export interface LabelFilterCondition { label: string; operator: string; value: string; } export interface UnrecognizedStage { type: 'unrecognized'; keyword: string; rest: string; } export interface MetricExpr { func: string; range: string; grouping?: Grouping; } export interface Grouping { by: boolean; // true = by, false = without labels: string[]; } export interface ParseError { message: string; start: number; end: number; } // ─── Completion situation ───────────────────────────────────────── export type SituationKind = 'NONE' | 'IN_SELECTOR_LABEL' | 'IN_SELECTOR_VALUE' | 'AFTER_PIPE' | 'IN_PIPELINE_ARG' | 'IN_LABEL_FILTER' | 'IN_FUNCTION_PARENS' | 'IN_GROUPING'; export interface Situation { kind: SituationKind; prefix: string; replaceRange: { start: number; end: number }; /** The label name when completing a selector value (IN_SELECTOR_VALUE). */ labelName?: string; /** The index of the matcher being edited within the stream selector (IN_SELECTOR_VALUE). */ matcherIndex?: number; /** True when cursor is inside an existing string literal (quotes already present). */ insideString?: boolean; }