Skip to content

Commit

Permalink
[Console] Add highlighting for painless language (elastic#202695)
Browse files Browse the repository at this point in the history
Closes elastic#201672

## 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"
      }
    }
  ]
}
```

<img width="1049" alt="Screenshot 2024-12-03 at 12 02 52"
src="https://github.com/user-attachments/assets/fb249953-a998-40c0-9775-3474e15b5de2">
  • Loading branch information
ElenaStoeva authored and CAWilson94 committed Dec 12, 2024
1 parent a47e506 commit c1c343e
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
71 changes: 71 additions & 0 deletions packages/kbn-monaco/src/console/lexer_rules/nested_painless.ts
Original file line number Diff line number Diff line change
@@ -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,
};
14 changes: 14 additions & 0 deletions packages/kbn-monaco/src/console/lexer_rules/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -16,11 +21,13 @@ export const consoleSharedLanguageConfiguration: monaco.languages.LanguageConfig
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
['"""', '"""\n'],
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '"""', close: '"""' },
],
Expand Down Expand Up @@ -100,17 +107,22 @@ 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.
*/
export const consoleSharedLexerRules: monaco.languages.IMonarchLanguage = {
...(globals as any),
defaultToken: 'invalid',
...sqlLanguageAttributes,
...painlessLanguageAttributes,
keywords: [...sqlLanguageAttributes.keywords, ...painlessLanguageAttributes.keywords],
tokenizer: {
root: [
// warning comment
Expand Down Expand Up @@ -138,5 +150,7 @@ export const consoleSharedLexerRules: monaco.languages.IMonarchLanguage = {
...xjsonRules,
// include sql rules
...sqlRules,
// include painless rules
...painlessRules,
},
};

0 comments on commit c1c343e

Please sign in to comment.