From 2fcd323927e42b4e7648f2aaa1a38dd3c7ea3900 Mon Sep 17 00:00:00 2001 From: Elena Stoeva <59341489+ElenaStoeva@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:48:16 +0000 Subject: [PATCH] [Console] Add highlighting for painless language (#202695) Closes https://github.com/elastic/kibana/issues/201672?reload=1?reload=1 ## Summary This PR adds existing painless rules to the console language so that the painless scripts are correctly highlighted. We are adding a painless starting rule that matches a string `"*_script"`, `"inline"`, or `"source"`, followed by a triple quote, in order to prevent clashing with the existing rules for triple-quote strings. Example request with a script: ``` POST _ingest/pipeline/_simulate { "pipeline": { "processors": [ { "script": { "description": "Extract 'tags' from 'env' field", "lang": "painless", "source": """ String[] envSplit = ctx['env'].splitOnToken(params['delimiter']); ArrayList tags = new ArrayList(); tags.add(envSplit[params['position']].trim()); ctx['tags'] = tags; """, "params": { "delimiter": "-", "position": 1 } } } ] }, "docs": [ { "_source": { "env": "es01-prod" } } ] } ``` Screenshot 2024-12-03 at 12 02 52 --- .../console/lexer_rules/nested_painless.ts | 71 +++++++++++++++++++ .../src/console/lexer_rules/shared.ts | 14 ++++ 2 files changed, 85 insertions(+) create mode 100644 packages/kbn-monaco/src/console/lexer_rules/nested_painless.ts diff --git a/packages/kbn-monaco/src/console/lexer_rules/nested_painless.ts b/packages/kbn-monaco/src/console/lexer_rules/nested_painless.ts new file mode 100644 index 0000000000000..8b4fcda56a64a --- /dev/null +++ b/packages/kbn-monaco/src/console/lexer_rules/nested_painless.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { lexerRules as painlessLexerRules } from '../../painless/lexer_rules'; + +/* + * This rule is used inside json root to start a painless highlighting sequence + */ +export const buildPainlessStartRule = (painlessRoot: string = 'painless_root') => { + return [ + /("(?:[^"]*_)?script"|"inline"|"source")(\s*?)(:)(\s*?)(""")/, + [ + 'variable', + 'whitespace', + 'punctuation.colon', + 'whitespace', + { + token: 'punctuation', + next: `@${painlessRoot}`, + }, + ], + ]; +}; + +/* + * This function creates a group of rules needed for painless highlighting in console. + * It reuses the lexer rules from the "painless" language, but since not all rules are referenced in the root + * tokenizer and to avoid conflicts with existing console rules, only selected rules are used. + */ +export const buildPainlessRules = (painlessRoot: string = 'painless_root') => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { root, comment, string_dq, string_sq } = painlessLexerRules.tokenizer; + return { + [painlessRoot]: [ + // the rule to end painless highlighting and get back to the previous tokenizer state + [ + /"""/, + { + token: 'punctuation', + next: '@pop', + }, + ], + ...root, + ], + comment, + string_dq, + string_sq, + }; +}; + +/* + * These language attributes need to be added to the console language definition for painless tokenizer + * to work correctly. + */ +export const painlessLanguageAttributes = { + keywords: painlessLexerRules.keywords, + primitives: painlessLexerRules.primitives, + constants: painlessLexerRules.constants, + operators: painlessLexerRules.operators, + symbols: painlessLexerRules.symbols, + digits: painlessLexerRules.digits, + octaldigits: painlessLexerRules.octaldigits, + binarydigits: painlessLexerRules.binarydigits, + hexdigits: painlessLexerRules.hexdigits, +}; diff --git a/packages/kbn-monaco/src/console/lexer_rules/shared.ts b/packages/kbn-monaco/src/console/lexer_rules/shared.ts index 62c8a28d31c65..b508d5954a3f7 100644 --- a/packages/kbn-monaco/src/console/lexer_rules/shared.ts +++ b/packages/kbn-monaco/src/console/lexer_rules/shared.ts @@ -8,6 +8,11 @@ */ import { buildSqlRules, buildSqlStartRule, sqlLanguageAttributes } from './nested_sql'; +import { + buildPainlessRules, + buildPainlessStartRule, + painlessLanguageAttributes, +} from './nested_painless'; import { monaco } from '../../..'; import { globals } from '../../common/lexer_rules'; import { buildXjsonRules } from '../../xjson/lexer_rules/xjson'; @@ -16,11 +21,13 @@ export const consoleSharedLanguageConfiguration: monaco.languages.LanguageConfig brackets: [ ['{', '}'], ['[', ']'], + ['(', ')'], ['"""', '"""\n'], ], autoClosingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, + { open: '(', close: ')' }, { open: '"', close: '"' }, { open: '"""', close: '"""' }, ], @@ -100,10 +107,13 @@ xjsonRules.json_root = [ matchToken('variable.template', /("\${\w+}")/), // @ts-expect-error include a rule to start sql highlighting buildSqlStartRule(), + // @ts-expect-error include a rule to start painless highlighting + buildPainlessStartRule(), ...xjsonRules.json_root, ]; const sqlRules = buildSqlRules(); +const painlessRules = buildPainlessRules(); /* Lexer rules that are shared between the Console editor and the Console output panel. */ @@ -111,6 +121,8 @@ export const consoleSharedLexerRules: monaco.languages.IMonarchLanguage = { ...(globals as any), defaultToken: 'invalid', ...sqlLanguageAttributes, + ...painlessLanguageAttributes, + keywords: [...sqlLanguageAttributes.keywords, ...painlessLanguageAttributes.keywords], tokenizer: { root: [ // warning comment @@ -138,5 +150,7 @@ export const consoleSharedLexerRules: monaco.languages.IMonarchLanguage = { ...xjsonRules, // include sql rules ...sqlRules, + // include painless rules + ...painlessRules, }, };