@elastic/monaco-esql
Version:
Monaco editor Monarch language syntax definitions for ES|QL
186 lines (185 loc) • 8.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.create = void 0;
const create = (deps = {}) => {
const { sourceCommands = [], processingCommands = [], options = [], literals = [], functions = [], delimiters = [], temporalUnits = [], } = deps;
const timeUnits = withLowercaseVariants(temporalUnits.flat()).sort((a, b) => (a > b ? -1 : 1));
return {
// Uncomment when developing.
// defaultToken: "invalid",
// ES|QL is case-insensitive.
ignoreCase: false,
// Lists of known language keywords and built-ins.
sourceCommands: withLowercaseVariants(sourceCommands),
processingCommands: withLowercaseVariants(processingCommands),
processingCommandsOnlyUppercase: processingCommands,
options: withLowercaseVariants(options),
literals: withLowercaseVariants(literals),
functions: withLowercaseVariants(functions),
delimiters,
namedOperators: withLowercaseVariants([
...(deps.operators?.named?.binary ?? []),
...(deps.operators?.named?.other ?? []),
]),
// Pre-defined regular expressions.
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
digits: /\d+(_+\d+)*/,
symbols: /[=><!~:&|+\-*\/\^%\.,]+/,
columnIdentifier: /[a-zA-Z0-9_\*\-]+/,
brackets: [
{ open: "[", close: "]", token: "delimiter.square" },
{ open: "(", close: ")", token: "delimiter.parenthesis" },
{ open: "{", close: "}", token: "delimiter.curly" },
{ open: "<", close: ">", token: "delimiter.angle" },
],
tokenizer: {
root: [
{ include: "@whitespace" },
// Keywords
[
/@?[a-zA-Z_$][\w$]*(?![\.\-:a-zA-Z_0-9])/,
{
cases: {
"@sourceCommands": { token: "keyword.command.source.$0" },
"@processingCommandsOnlyUppercase": { token: "keyword.command.processing.$0" },
"@options": { token: "keyword.option.$0" },
"@literals": { token: "keyword.literal.$0" },
"@functions": { token: "identifier.function.$0" },
"@namedOperators": { token: "keyword.operator.$0" },
"\\@{1}timestamp": "identifier.timestamp",
"@default": "identifier",
},
},
],
{ include: "@expression" },
{ include: "@processingCommand" },
[/\[|\(|\)|\]/, "@brackets"],
[
/@symbols/,
{
cases: {
"@delimiters": "delimiter",
"@default": "",
},
},
],
],
// --------------------------------- Hidden channel: whitespace and comments
whitespace: [
[/[ \t\r\n]+/, ""],
[/\/\*\*(?!\/)/, "comment.doc", "@doc"],
[/\/\*/, "comment", "@comment"],
[/\/\/.*$/, "comment"],
],
comment: [
[/[^\/*]+/, "comment"],
[/\*\//, "comment", "@pop"],
[/[\/*]/, "comment"],
],
doc: [
[/[^\/*]+/, "comment.doc"],
[/\*\//, "comment.doc", "@pop"],
[/[\/*]/, "comment.doc"],
],
// ---------------------------------------------------------------- Commands
// This code block allows to color commands when they are followed by a pipe
// character "|", this way all new processing commands are color even if
// they are not part of the command name list.
processingCommand: [
[
/\|/,
{ token: "delimiter.pipe", switchTo: "@beforeMnemonicWhitespace" },
],
],
beforeMnemonicWhitespace: [
{ include: "@whitespace" },
["", { token: "", switchTo: "@commandName" }],
],
// Matches *command name*, i.e. the mnemonic.
commandName: [
// First tries to match all known command names.
[
sourceCommands.join("|"),
{ token: "keyword.command.source.$0", switchTo: "@root" },
],
[
processingCommands.join("|"),
{ token: "keyword.command.processing.$0", switchTo: "@root" },
],
// If command name is not well known, just matches the first word.
[
/\w+\b/,
{ token: "keyword.command.processing.$0", switchTo: "@root" },
],
],
// ------------------------------------------------------------- Expressions
expression: [
{ include: "@whitespace" },
{ include: "@literal" },
// Basic ES|QL columns (fields), e.g.: "field", "nested.field", etc.
[/(@columnIdentifier)(\.(@columnIdentifier))*/, "identifier.column"],
// Inline casts: 1.3::INTEGER
[/::\w+\b/, "type"],
// strings
[/"""/, "string.triple", "@string_triple"],
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-terminated string
[/'([^'\\]|\\.)*$/, "string.invalid"], // non-terminated string
[/"/, "string", "@string"],
// Escaped column parts: nested.`escaped`.column
[/`/, "string", "@column_escape_part"],
],
literal: [
{ include: "@timeInterval" },
{ include: "@number" },
// Params
[
/\?{1,9}(([a-zA-Z_][a-zA-Z_0-9]+)|[0-9]+)?/,
{
cases: {
"\\?{1,9}": "variable.name.unnamed",
"\\?{1,9}([a-zA-Z_][a-zA-Z_0-9]+)?": "variable.name.named",
"\\?{1,9}([0-9]+)?": "variable.name.positional",
},
},
],
],
timeInterval: [[`(@digits)\\s*(${timeUnits.join("|")})`, "number.time"]],
number: [
[/(@digits)[eE]([\-+]?(@digits))?/, "number.float"],
[/(@digits)?\.(@digits)([eE][\-+]?(@digits))?/, "number.float"],
[/(@digits)/, "number"],
],
// Single double-quote strings: "str"
string: [
[/[^\\"]+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/"/, "string", "@pop"],
],
// Triple double-quoted strings: """str"""
string_triple: [
[/"""/, "string.triple", "@pop"],
[/[^"]+/, "string.triple"],
],
// Backtick quoted "strings". ES|QL does not have back-tick "strings"
// *per se*, but column parts can have backtick-escaped parts.
column_escape_part: [
[/[^`]+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/`/, "string", "@pop"],
],
},
};
};
exports.create = create;
/**
* Given a list of strings, returns a new list with both the original and their lowercase versions (no duplicates).
*/
function withLowercaseVariants(list) {
const set = new Set(list);
for (const item of list) {
set.add(item.toLowerCase());
}
return Array.from(set);
}