[TSVB] Replaces EuiCodeEditor 👉 Monaco editor (#100684) (#102665)

* Сhanged EuiCodeEditor to CodeEditor (monaco) at markdown_editor.js

* Added css lang support for monaco-editor.

* Added .d.ts for css lang import directly from monaco.

* Moved handlebars_url language to the code_editor.

Moved handlebars_url language registration to the code_editor.
Changed the way of registration of languages.

* Added merge for markdown_handlebars lang.

* Changed to simple markdown syntax.

Handlebars syntax breaks highlighting of special chars in markdown syntax.

* Removed useless mergeConfig function.

* Removed legacy import.

* Refactor export from monaco-editor.

* Fixed 'Could not find a declaration file for module'

* Fixed tests.

* Fixed typings errors.

* Added comment to typings.

* Fixed clearMarkdown for Monaco editor.

* Made changes based on suggestions.

* Fixed types errors.

* Fixed function tests types errors.

* Fixes, based on nits.

* Fixes based on nits.

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Yaroslav Kuznietsov <kuznetsov.yaroslav.yk@gmail.com>
This commit is contained in:
Kibana Machine 2021-06-18 16:33:05 -04:00 committed by GitHub
parent b4cd270e93
commit b85a234d54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 204 additions and 56 deletions

View file

@ -6,7 +6,8 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { LangModule as LangModuleType } from '../types';
import { ID } from './constants'; import { ID } from './constants';
import { lexerRules } from './lexer_rules'; import { lexerRules } from './lexer_rules';
export const EsqlLang = { ID, lexerRules }; export const EsqlLang: LangModuleType = { ID, lexerRules };

View file

@ -0,0 +1,21 @@
/*
* 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 './monaco_imports';
import { LangModule as LangModuleType } from './types';
function registerLanguage(language: LangModuleType) {
const { ID, lexerRules, languageConfiguration } = language;
monaco.languages.register({ id: ID });
monaco.languages.setMonarchTokensProvider(ID, lexerRules);
if (languageConfiguration) {
monaco.languages.setLanguageConfiguration(ID, languageConfiguration);
}
}
export { registerLanguage };

View file

@ -12,7 +12,13 @@ import './register_globals';
export { monaco } from './monaco_imports'; export { monaco } from './monaco_imports';
export { XJsonLang } from './xjson'; export { XJsonLang } from './xjson';
export { PainlessLang, PainlessContext, PainlessAutocompleteField } from './painless'; export { PainlessLang, PainlessContext, PainlessAutocompleteField } from './painless';
/* eslint-disable-next-line @kbn/eslint/module_migration */ /* eslint-disable-next-line @kbn/eslint/module_migration */
import * as BarePluginApi from 'monaco-editor/esm/vs/editor/editor.api'; import * as BarePluginApi from 'monaco-editor/esm/vs/editor/editor.api';
export { BarePluginApi };
import { registerLanguage } from './helpers';
import {
LangModule as LangModuleType,
CompleteLangModule as CompleteLangModuleType,
} from './types';
export { BarePluginApi, registerLanguage, LangModuleType, CompleteLangModuleType };

View file

@ -9,8 +9,9 @@
import { ID } from './constants'; import { ID } from './constants';
import { lexerRules, languageConfiguration } from './lexer_rules'; import { lexerRules, languageConfiguration } from './lexer_rules';
import { getSuggestionProvider, getSyntaxErrors } from './language'; import { getSuggestionProvider, getSyntaxErrors } from './language';
import { CompleteLangModule as CompleteLangModuleType } from '../types';
export const PainlessLang = { export const PainlessLang: CompleteLangModuleType = {
ID, ID,
getSuggestionProvider, getSuggestionProvider,
lexerRules, lexerRules,

View file

@ -10,6 +10,8 @@ import { XJsonLang } from './xjson';
import { PainlessLang } from './painless'; import { PainlessLang } from './painless';
import { EsqlLang } from './esql'; import { EsqlLang } from './esql';
import { monaco } from './monaco_imports'; import { monaco } from './monaco_imports';
import { registerLanguage } from './helpers';
// @ts-ignore // @ts-ignore
import xJsonWorkerSrc from '!!raw-loader!../../target_web/xjson.editor.worker.js'; import xJsonWorkerSrc from '!!raw-loader!../../target_web/xjson.editor.worker.js';
// @ts-ignore // @ts-ignore
@ -20,14 +22,9 @@ import painlessWorkerSrc from '!!raw-loader!../../target_web/painless.editor.wor
/** /**
* Register languages and lexer rules * Register languages and lexer rules
*/ */
monaco.languages.register({ id: XJsonLang.ID }); registerLanguage(XJsonLang);
monaco.languages.setMonarchTokensProvider(XJsonLang.ID, XJsonLang.lexerRules); registerLanguage(PainlessLang);
monaco.languages.setLanguageConfiguration(XJsonLang.ID, XJsonLang.languageConfiguration); registerLanguage(EsqlLang);
monaco.languages.register({ id: PainlessLang.ID });
monaco.languages.setMonarchTokensProvider(PainlessLang.ID, PainlessLang.lexerRules);
monaco.languages.setLanguageConfiguration(PainlessLang.ID, PainlessLang.languageConfiguration);
monaco.languages.register({ id: EsqlLang.ID });
monaco.languages.setMonarchTokensProvider(EsqlLang.ID, EsqlLang.lexerRules);
/** /**
* Create web workers by language ID * Create web workers by language ID

View file

@ -0,0 +1,22 @@
/*
* 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 './monaco_imports';
export interface LangModule {
ID: string;
lexerRules: monaco.languages.IMonarchLanguage;
languageConfiguration?: monaco.languages.LanguageConfiguration;
getSuggestionProvider?: Function;
getSyntaxErrors?: Function;
}
export interface CompleteLangModule extends LangModule {
languageConfiguration: monaco.languages.LanguageConfiguration;
getSuggestionProvider: Function;
getSyntaxErrors: Function;
}

View file

@ -12,5 +12,6 @@
import './language'; import './language';
import { ID } from './constants'; import { ID } from './constants';
import { lexerRules, languageConfiguration } from './lexer_rules'; import { lexerRules, languageConfiguration } from './lexer_rules';
import { LangModule as LangModuleType } from '../types';
export const XJsonLang = { ID, lexerRules, languageConfiguration }; export const XJsonLang: LangModuleType = { ID, lexerRules, languageConfiguration };

View file

@ -17,6 +17,9 @@ import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
import { useUiSetting } from '../ui_settings'; import { useUiSetting } from '../ui_settings';
import { Props } from './code_editor'; import { Props } from './code_editor';
import './register_languages';
export * from './languages';
const LazyBaseEditor = React.lazy(() => import('./code_editor')); const LazyBaseEditor = React.lazy(() => import('./code_editor'));

View file

@ -6,4 +6,4 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
export const LANG = 'handlebars_url'; export const LANG = 'css';

View file

@ -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 { lexerRules, languageConfiguration } from './language';
import { LANG } from './constants';
export const Lang: LangModuleType = { ID: LANG, lexerRules, languageConfiguration };

View file

@ -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.
*/
/* eslint-disable @kbn/eslint/module_migration */
import { conf, language } from 'monaco-editor/esm/vs/basic-languages/css/css';
export { conf as languageConfiguration, language as lexerRules };

View file

@ -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 = 'handlebars';

View file

@ -0,0 +1,13 @@
/*
* 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';
import { LANG } from './constants';
export const Lang: LangModuleType = { ID: LANG, languageConfiguration, lexerRules };

View file

@ -13,7 +13,7 @@
import { monaco } from '@kbn/monaco'; import { monaco } from '@kbn/monaco';
export const conf: monaco.languages.LanguageConfiguration = { export const languageConfiguration: monaco.languages.LanguageConfiguration = {
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
comments: { comments: {
@ -42,7 +42,7 @@ export const conf: monaco.languages.LanguageConfiguration = {
], ],
}; };
export const language: monaco.languages.IMonarchLanguage = { export const lexerRules: monaco.languages.IMonarchLanguage = {
// Set defaultToken to invalid to see what you do not tokenize yet. // Set defaultToken to invalid to see what you do not tokenize yet.
defaultToken: 'invalid', defaultToken: 'invalid',
tokenPostfix: '', tokenPostfix: '',

View file

@ -0,0 +1,13 @@
/*
* 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 { Lang as CssLang } from './css';
import { Lang as HandlebarsLang } from './handlebars';
import { Lang as MarkdownLang } from './markdown';
export { CssLang, HandlebarsLang, MarkdownLang };

View file

@ -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 = 'markdown';

View file

@ -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';
import { LANG } from './constants';
export const Lang: LangModuleType = { ID: LANG, languageConfiguration, lexerRules };

View file

@ -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.
*/
/* eslint-disable @kbn/eslint/module_migration */
import { conf, language } from 'monaco-editor/esm/vs/basic-languages/markdown/markdown';
export { conf as languageConfiguration, language as lexerRules };

View file

@ -0,0 +1,13 @@
/*
* 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 { registerLanguage } from '@kbn/monaco';
import { CssLang, HandlebarsLang, MarkdownLang } from './languages';
registerLanguage(CssLang);
registerLanguage(HandlebarsLang);
registerLanguage(MarkdownLang);

View file

@ -9,18 +9,10 @@
import * as React from 'react'; import * as React from 'react';
import { monaco } from '@kbn/monaco'; import { monaco } from '@kbn/monaco';
import { Props as CodeEditorProps } from '../code_editor/code_editor'; import { Props as CodeEditorProps } from '../code_editor/code_editor';
import { CodeEditor } from '../code_editor'; import { CodeEditor, HandlebarsLang } from '../code_editor';
import { LANG } from './constants';
import { language, conf } from './language';
import './styles.scss'; import './styles.scss';
monaco.languages.register({
id: LANG,
});
monaco.languages.setMonarchTokensProvider(LANG, language);
monaco.languages.setLanguageConfiguration(LANG, conf);
export interface UrlTemplateEditorVariable { export interface UrlTemplateEditorVariable {
label: string; label: string;
title?: string; title?: string;
@ -74,7 +66,7 @@ export const UrlTemplateEditor: React.FC<UrlTemplateEditorProps> = ({
return; return;
} }
const { dispose } = monaco.languages.registerCompletionItemProvider(LANG, { const { dispose } = monaco.languages.registerCompletionItemProvider(HandlebarsLang.ID, {
triggerCharacters: ['{', '/', '?', '&', '='], triggerCharacters: ['{', '/', '?', '&', '='],
provideCompletionItems(model, position, context, token) { provideCompletionItems(model, position, context, token) {
const { lineNumber } = position; const { lineNumber } = position;
@ -132,7 +124,7 @@ export const UrlTemplateEditor: React.FC<UrlTemplateEditorProps> = ({
return ( return (
<div className={'urlTemplateEditor__container'} onKeyDown={handleKeyDown}> <div className={'urlTemplateEditor__container'} onKeyDown={handleKeyDown}>
<Editor <Editor
languageId={LANG} languageId={HandlebarsLang.ID}
height={height} height={height}
value={value} value={value}
onChange={onChange} onChange={onChange}

View file

@ -15,10 +15,9 @@ import React, { Component } from 'react';
import { createTickFormatter } from './lib/tick_formatter'; import { createTickFormatter } from './lib/tick_formatter';
import { convertSeriesToVars } from './lib/convert_series_to_vars'; import { convertSeriesToVars } from './lib/convert_series_to_vars';
import _ from 'lodash'; import _ from 'lodash';
import 'brace/mode/markdown'; import { CodeEditor, MarkdownLang } from '../../../../kibana_react/public';
import 'brace/theme/github';
import { EuiText, EuiCodeBlock, EuiSpacer, EuiTitle, EuiCodeEditor } from '@elastic/eui'; import { EuiText, EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
@ -101,14 +100,13 @@ export class MarkdownEditor extends Component {
return ( return (
<div className="tvbMarkdownEditor"> <div className="tvbMarkdownEditor">
<div className="tvbMarkdownEditor__editor"> <div className="tvbMarkdownEditor__editor">
<EuiCodeEditor <CodeEditor
onLoad={this.handleOnLoad} editorDidMount={this.handleOnLoad}
mode="markdown" languageId={MarkdownLang.ID}
theme="github" options={{
width="100%" fontSize: '14px',
height="100%" wordWrap: 'on',
name={`ace-${model.id}`} }}
setOptions={{ wrap: true, fontSize: '14px' }}
value={model.markdown} value={model.markdown}
onChange={this.handleChange} onChange={this.handleChange}
/> />

View file

@ -20,7 +20,6 @@ import {
EuiSpacer, EuiSpacer,
EuiTitle, EuiTitle,
EuiHorizontalRule, EuiHorizontalRule,
EuiCodeEditor,
} from '@elastic/eui'; } from '@elastic/eui';
// @ts-expect-error // @ts-expect-error
import less from 'less/lib/less-browser'; import less from 'less/lib/less-browser';
@ -43,6 +42,7 @@ import { getDefaultQueryLanguage } from '../lib/get_default_query_language';
import { VisDataContext } from '../../contexts/vis_data_context'; import { VisDataContext } from '../../contexts/vis_data_context';
import { PanelConfigProps, PANEL_CONFIG_TABS } from './types'; import { PanelConfigProps, PANEL_CONFIG_TABS } from './types';
import { TimeseriesVisParams } from '../../../types'; import { TimeseriesVisParams } from '../../../types';
import { CodeEditor, CssLang } from '../../../../../kibana_react/public';
const lessC = less(window, { env: 'production' }); const lessC = less(window, { env: 'production' });
@ -281,12 +281,10 @@ export class MarkdownPanelConfig extends Component<
</span> </span>
</EuiTitle> </EuiTitle>
<EuiSpacer size="s" /> <EuiSpacer size="s" />
<EuiCodeEditor <CodeEditor
mode="less" height="500px"
theme="github" languageId={CssLang.ID}
width="100%" options={{ fontSize: 14 }}
name={`ace-css-${model.id}`}
setOptions={{ fontSize: '14px' }}
value={model.markdown_less ?? ''} value={model.markdown_less ?? ''}
onChange={this.handleCSSChange} onChange={this.handleCSSChange}
/> />

View file

@ -132,19 +132,18 @@ export class VisualBuilderPageObject extends FtrService {
} }
public async clearMarkdown() { public async clearMarkdown() {
// Since we use ACE editor and that isn't really storing its value inside
// a textarea we must really select all text and remove it, and cannot use
// clearValue().
await this.retry.waitForWithTimeout('text area is cleared', 20000, async () => { await this.retry.waitForWithTimeout('text area is cleared', 20000, async () => {
const editor = await this.testSubjects.find('codeEditorContainer'); const input = await this.find.byCssSelector('.tvbMarkdownEditor__editor textarea');
const $ = await editor.parseDomContent(); await input.clickMouseButton();
const value = $('.ace_line').text(); await input.clearValueWithKeyboard();
if (value.length > 0) {
this.log.debug('Clearing text area input');
this.waitForMarkdownTextAreaCleaned();
}
return value.length === 0; const linesContainer = await this.find.byCssSelector(
'.tvbMarkdownEditor__editor .view-lines'
);
// lines of code in monaco-editor
// text is not present in textarea
const lines = await linesContainer.findAllByClassName('mtk1');
return lines.length === 0;
}); });
} }

4
typings/index.d.ts vendored
View file

@ -33,3 +33,7 @@ declare module 'axios/lib/adapters/xhr';
// See https://github.com/storybookjs/storybook/issues/11684 // See https://github.com/storybookjs/storybook/issues/11684
declare module 'react-syntax-highlighter/dist/cjs/create-element'; declare module 'react-syntax-highlighter/dist/cjs/create-element';
declare module 'react-syntax-highlighter/dist/cjs/prism-light'; declare module 'react-syntax-highlighter/dist/cjs/prism-light';
// Monaco languages support
declare module 'monaco-editor/esm/vs/basic-languages/markdown/markdown';
declare module 'monaco-editor/esm/vs/basic-languages/css/css';