mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[CodeEditor] Add grok highlighting (#159334)
## Summary
Close https://github.com/elastic/kibana/issues/155506
<img width="971" alt="Screenshot 2023-06-12 at 17 11 18"
src="7522bcba
-e3aa-4229-93b2-27d12a335426">
I didn't introduce any custom colors here but just used built-in token
types for highlighting (`string` for red and `variable` for blue)
This commit is contained in:
parent
2b42341d04
commit
e89313327e
11 changed files with 220 additions and 65 deletions
|
@ -11,3 +11,4 @@ export { LANG as MarkdownLang } from './markdown/constants';
|
|||
export { LANG as YamlLang } from './yaml/constants';
|
||||
export { LANG as HandlebarsLang } from './handlebars/constants';
|
||||
export { LANG as HJsonLang } from './hjson/constants';
|
||||
export { LANG as GrokLang } from './grok/constants';
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const LANG = 'grok';
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { LangModuleType } from '@kbn/monaco';
|
||||
import { languageConfiguration, lexerRules } from './language';
|
||||
|
||||
export const Lang: LangModuleType = { ID: 'grok', languageConfiguration, lexerRules };
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { monaco, registerLanguage } from '@kbn/monaco';
|
||||
import { Lang } from '.';
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
window.ResizeObserver = class ResizeObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
};
|
||||
|
||||
registerLanguage(Lang);
|
||||
});
|
||||
|
||||
test('lang', () => {
|
||||
expect(monaco.editor.tokenize('\\[(?:-|%{NUMBER:bytes:int})\\]', 'grok')).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 0,
|
||||
"type": "string.escape.grokEscape.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 1,
|
||||
"type": "source.grokEscaped.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 2,
|
||||
"type": "regexp.grokRegex.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 5,
|
||||
"type": "source.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 6,
|
||||
"type": "regexp.grokRegex.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 7,
|
||||
"type": "string.openGrok.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 9,
|
||||
"type": "variable.syntax.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 15,
|
||||
"type": "string.separator.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 16,
|
||||
"type": "variable.id.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 21,
|
||||
"type": "string.separator.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 22,
|
||||
"type": "variable.type.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 25,
|
||||
"type": "string.closeGrok.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 26,
|
||||
"type": "regexp.grokRegex.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 27,
|
||||
"type": "string.escape.grokEscape.grok",
|
||||
},
|
||||
Token {
|
||||
"language": "grok",
|
||||
"offset": 28,
|
||||
"type": "source.grokEscaped.grok",
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { monaco } from '@kbn/monaco';
|
||||
|
||||
export const languageConfiguration: monaco.languages.LanguageConfiguration = {
|
||||
brackets: [
|
||||
['{', '}'],
|
||||
['[', ']'],
|
||||
['(', ')'],
|
||||
],
|
||||
|
||||
autoClosingPairs: [
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '"', close: '"' },
|
||||
{ open: "'", close: "'" },
|
||||
],
|
||||
|
||||
surroundingPairs: [
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '"', close: '"' },
|
||||
{ open: "'", close: "'" },
|
||||
],
|
||||
};
|
||||
|
||||
export const lexerRules: monaco.languages.IMonarchLanguage = {
|
||||
// special grok symbols []()?:|
|
||||
grokRegexSymbols: /[\[\]()?:|]/,
|
||||
|
||||
tokenizer: {
|
||||
root: [
|
||||
// %{SYNTAX}
|
||||
[/(%\{)([^:}]+)(})/, ['string.openGrok', 'variable.syntax', 'string.closeGrok']],
|
||||
|
||||
// %{SYNTAX:ID}
|
||||
[
|
||||
/(%\{)([^:}]+)(:)([^:}]+)(})/,
|
||||
[
|
||||
'string.openGrok',
|
||||
'variable.syntax',
|
||||
'string.separator',
|
||||
'variable.id',
|
||||
'string.closeGrok',
|
||||
],
|
||||
],
|
||||
|
||||
// %{SYNTAX:ID:TYPE}
|
||||
[
|
||||
/(%\{)([^:}]+)(:)([^:}]+)(:)([^:}]+)(})/,
|
||||
[
|
||||
'string.openGrok',
|
||||
'variable.syntax',
|
||||
'string.separator',
|
||||
'variable.id',
|
||||
'string.separator',
|
||||
'variable.type',
|
||||
'string.closeGrok',
|
||||
],
|
||||
],
|
||||
|
||||
[/(\\)(@grokRegexSymbols)/, ['string.escape.grokEscape', 'source.grokEscaped']], // highlight escape symbol and don't highlight escaped symbol
|
||||
[/@grokRegexSymbols/, 'regexp.grokRegex'], // highlight special grok symbols
|
||||
],
|
||||
},
|
||||
};
|
|
@ -11,5 +11,6 @@ import { Lang as HandlebarsLang } from './handlebars';
|
|||
import { Lang as MarkdownLang } from './markdown';
|
||||
import { Lang as YamlLang } from './yaml';
|
||||
import { Lang as HJson } from './hjson';
|
||||
import { Lang as GrokLang } from './grok';
|
||||
|
||||
export { CssLang, HandlebarsLang, MarkdownLang, YamlLang, HJson };
|
||||
export { CssLang, HandlebarsLang, MarkdownLang, YamlLang, HJson, GrokLang };
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import { registerLanguage } from '@kbn/monaco';
|
||||
import { CssLang, HandlebarsLang, MarkdownLang, YamlLang, HJson } from './languages';
|
||||
import { CssLang, HandlebarsLang, MarkdownLang, YamlLang, HJson, GrokLang } from './languages';
|
||||
|
||||
registerLanguage(CssLang);
|
||||
registerLanguage(HandlebarsLang);
|
||||
registerLanguage(MarkdownLang);
|
||||
registerLanguage(YamlLang);
|
||||
registerLanguage(HJson);
|
||||
registerLanguage(GrokLang);
|
||||
|
|
|
@ -13,6 +13,7 @@ export {
|
|||
YamlLang,
|
||||
HandlebarsLang,
|
||||
HJsonLang,
|
||||
GrokLang,
|
||||
CodeEditor,
|
||||
CodeEditorField,
|
||||
} from './code_editor';
|
||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFormRow } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { CodeEditor } from '@kbn/kibana-react-plugin/public';
|
||||
import { CodeEditor, GrokLang } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
export function PatternInput({ value, onChange }) {
|
||||
return (
|
||||
|
@ -21,7 +21,7 @@ export function PatternInput({ value, onChange }) {
|
|||
data-test-subj="patternInput"
|
||||
>
|
||||
<CodeEditor
|
||||
languageId="plaintext"
|
||||
languageId={GrokLang}
|
||||
value={value}
|
||||
height={200}
|
||||
options={{
|
||||
|
|
|
@ -67,42 +67,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
expect(response).to.eql(testData);
|
||||
});
|
||||
|
||||
// This test will need to be fixed.
|
||||
it.skip('applies the correct CSS classes', async () => {
|
||||
const grokPattern = '\\[(?:-|%{NUMBER:bytes:int})\\]';
|
||||
|
||||
await PageObjects.grokDebugger.setPatternInput(grokPattern);
|
||||
|
||||
const GROK_START = 'grokStart';
|
||||
const GROK_PATTERN_NAME = 'grokPatternName';
|
||||
const GROK_SEPARATOR = 'grokSeparator';
|
||||
const GROK_FIELD_NAME = 'grokFieldName';
|
||||
const GROK_FIELD_TYPE = 'grokFieldType';
|
||||
const GROK_END = 'grokEnd';
|
||||
const GROK_ESCAPE = 'grokEscape';
|
||||
const GROK_ESCAPED = 'grokEscaped';
|
||||
const GROK_REGEX = 'grokRegex';
|
||||
|
||||
await PageObjects.grokDebugger.assertPatternInputSyntaxHighlighting([
|
||||
{ token: GROK_ESCAPE, content: '\\' },
|
||||
{ token: GROK_ESCAPED, content: '[' },
|
||||
{ token: GROK_REGEX, content: '(' },
|
||||
{ token: GROK_REGEX, content: '?' },
|
||||
{ token: GROK_REGEX, content: ':' },
|
||||
{ token: GROK_REGEX, content: '|' },
|
||||
{ token: GROK_START, content: '%{' },
|
||||
{ token: GROK_PATTERN_NAME, content: 'NUMBER' },
|
||||
{ token: GROK_SEPARATOR, content: ':' },
|
||||
{ token: GROK_FIELD_NAME, content: 'bytes' },
|
||||
{ token: GROK_SEPARATOR, content: ':' },
|
||||
{ token: GROK_FIELD_TYPE, content: 'int' },
|
||||
{ token: GROK_END, content: '}' },
|
||||
{ token: GROK_REGEX, content: ')' },
|
||||
{ token: GROK_ESCAPE, content: '\\' },
|
||||
{ token: GROK_ESCAPED, content: ']' },
|
||||
]);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await security.testUser.restoreDefaults();
|
||||
});
|
||||
|
|
|
@ -51,29 +51,4 @@ export class GrokDebuggerPageObject extends FtrService {
|
|||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
// This needs to be fixed to work with the new test functionality. This method is skipped currently.
|
||||
async assertPatternInputSyntaxHighlighting(expectedHighlights: any[]) {
|
||||
const patternInputElement = await this.testSubjects.find(
|
||||
'grokDebuggerContainer > acePatternInput > codeEditorContainer'
|
||||
);
|
||||
const highlightedElements = await patternInputElement.findAllByXpath(
|
||||
'.//div[@class="ace_line"]/*'
|
||||
);
|
||||
|
||||
expect(highlightedElements.length).to.be(expectedHighlights.length);
|
||||
await Promise.all(
|
||||
highlightedElements.map(async (element: any, index) => {
|
||||
const highlightClass = await element.getAttribute('class');
|
||||
const highlightedContent = await element.getVisibleText();
|
||||
|
||||
const expectedHighlight = expectedHighlights[index];
|
||||
const expectedHighlightClass = `ace_${expectedHighlight.token}`;
|
||||
const expectedHighlightedContent = expectedHighlight.content;
|
||||
|
||||
expect(highlightClass).to.be(expectedHighlightClass);
|
||||
expect(highlightedContent).to.be(expectedHighlightedContent);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue