mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Monaco] Add JSON syntax support to the Monaco editor (#143739)
* Add JSON syntax support to the Monaco editor * Bump `monaco-editor` version * Fix the `monaco` package and usages to initialize lazily * Add a story demonstrating JSON schema usage
This commit is contained in:
parent
f5acf76351
commit
9456303c97
12 changed files with 151 additions and 88 deletions
|
@ -558,7 +558,7 @@
|
|||
"moment": "^2.29.4",
|
||||
"moment-duration-format": "^2.3.2",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"monaco-editor": "^0.22.3",
|
||||
"monaco-editor": "^0.24.0",
|
||||
"mustache": "^2.3.2",
|
||||
"node-fetch": "^2.6.7",
|
||||
"node-forge": "^1.3.1",
|
||||
|
|
|
@ -12,10 +12,12 @@ 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);
|
||||
}
|
||||
monaco.languages.onLanguage(ID, () => {
|
||||
monaco.languages.setMonarchTokensProvider(ID, lexerRules);
|
||||
if (languageConfiguration) {
|
||||
monaco.languages.setLanguageConfiguration(ID, languageConfiguration);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { registerLanguage };
|
||||
|
|
|
@ -23,6 +23,7 @@ import 'monaco-editor/esm/vs/editor/contrib/hover/hover.js'; // Needed for hover
|
|||
import 'monaco-editor/esm/vs/editor/contrib/parameterHints/parameterHints.js'; // Needed for signature
|
||||
import 'monaco-editor/esm/vs/editor/contrib/bracketMatching/bracketMatching.js'; // Needed for brackets matching highlight
|
||||
|
||||
import 'monaco-editor/esm/vs/language/json/monaco.contribution.js';
|
||||
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js'; // Needed for basic javascript support
|
||||
import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution.js'; // Needed for basic xml support
|
||||
|
||||
|
|
|
@ -12,11 +12,9 @@ import { EsqlLang } from './esql';
|
|||
import { monaco } from './monaco_imports';
|
||||
import { registerLanguage } from './helpers';
|
||||
|
||||
// @ts-ignore
|
||||
import jsonWorkerSrc from '!!raw-loader!../../target_workers/json.editor.worker.js';
|
||||
import xJsonWorkerSrc from '!!raw-loader!../../target_workers/xjson.editor.worker.js';
|
||||
// @ts-ignore
|
||||
import defaultWorkerSrc from '!!raw-loader!../../target_workers/default.editor.worker.js';
|
||||
// @ts-ignore
|
||||
import painlessWorkerSrc from '!!raw-loader!../../target_workers/painless.editor.worker.js';
|
||||
|
||||
/**
|
||||
|
@ -32,6 +30,7 @@ registerLanguage(EsqlLang);
|
|||
const mapLanguageIdToWorker: { [key: string]: any } = {
|
||||
[XJsonLang.ID]: xJsonWorkerSrc,
|
||||
[PainlessLang.ID]: painlessWorkerSrc,
|
||||
[monaco.languages.json.jsonDefaults.languageId]: jsonWorkerSrc,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
|
|
14
packages/kbn-monaco/src/worker.d.ts
vendored
Normal file
14
packages/kbn-monaco/src/worker.d.ts
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare module '!!raw-loader!*.editor.worker.js' {
|
||||
const contents: string;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default contents;
|
||||
}
|
|
@ -12,16 +12,12 @@ import { monaco } from '../monaco_imports';
|
|||
import { WorkerProxyService } from './worker_proxy_service';
|
||||
import { ID } from './constants';
|
||||
|
||||
const wps = new WorkerProxyService();
|
||||
|
||||
monaco.languages.onLanguage(ID, async () => {
|
||||
return wps.setup();
|
||||
});
|
||||
|
||||
const OWNER = 'XJSON_GRAMMAR_CHECKER';
|
||||
|
||||
export const registerGrammarChecker = () => {
|
||||
const allDisposables: monaco.IDisposable[] = [];
|
||||
monaco.languages.onLanguage(ID, async () => {
|
||||
const wps = new WorkerProxyService();
|
||||
|
||||
wps.setup();
|
||||
|
||||
const updateAnnotations = async (model: monaco.editor.IModel): Promise<void> => {
|
||||
if (model.isDisposed()) {
|
||||
|
@ -50,21 +46,20 @@ export const registerGrammarChecker = () => {
|
|||
};
|
||||
|
||||
const onModelAdd = (model: monaco.editor.IModel) => {
|
||||
if (model.getModeId() === ID) {
|
||||
allDisposables.push(
|
||||
model.onDidChangeContent(async () => {
|
||||
updateAnnotations(model);
|
||||
})
|
||||
);
|
||||
|
||||
updateAnnotations(model);
|
||||
if (model.getModeId() !== ID) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
allDisposables.push(monaco.editor.onDidCreateModel(onModelAdd));
|
||||
return () => {
|
||||
wps.stop();
|
||||
allDisposables.forEach((d) => d.dispose());
|
||||
};
|
||||
};
|
||||
|
||||
registerGrammarChecker();
|
||||
const { dispose } = model.onDidChangeContent(async () => {
|
||||
updateAnnotations(model);
|
||||
});
|
||||
|
||||
model.onWillDispose(() => {
|
||||
dispose();
|
||||
});
|
||||
|
||||
updateAnnotations(model);
|
||||
};
|
||||
|
||||
monaco.editor.onDidCreateModel(onModelAdd);
|
||||
});
|
||||
|
|
|
@ -8,43 +8,43 @@
|
|||
|
||||
const path = require('path');
|
||||
|
||||
const createLangWorkerConfig = (lang) => {
|
||||
const entry =
|
||||
lang === 'default'
|
||||
? 'monaco-editor/esm/vs/editor/editor.worker.js'
|
||||
: path.resolve(__dirname, 'src', lang, 'worker', `${lang}.worker.ts`);
|
||||
|
||||
return {
|
||||
mode: 'production',
|
||||
entry,
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'target_workers'),
|
||||
filename: `${lang}.editor.worker.js`,
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.tsx'],
|
||||
},
|
||||
stats: 'errors-only',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|ts)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
babelrc: false,
|
||||
presets: [require.resolve('@kbn/babel-preset/webpack_preset')],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const getWorkerEntry = (language) => {
|
||||
switch (language) {
|
||||
case 'default':
|
||||
return 'monaco-editor/esm/vs/editor/editor.worker.js';
|
||||
case 'json':
|
||||
return 'monaco-editor/esm/vs/language/json/json.worker.js';
|
||||
default:
|
||||
return path.resolve(__dirname, 'src', language, 'worker', `${language}.worker.ts`);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = [
|
||||
createLangWorkerConfig('xjson'),
|
||||
createLangWorkerConfig('painless'),
|
||||
createLangWorkerConfig('default'),
|
||||
];
|
||||
const getWorkerConfig = (language) => ({
|
||||
mode: 'production',
|
||||
entry: getWorkerEntry(language),
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'target_workers'),
|
||||
filename: `${language}.editor.worker.js`,
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.tsx'],
|
||||
},
|
||||
stats: 'errors-only',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|ts)$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
babelrc: false,
|
||||
presets: [require.resolve('@kbn/babel-preset/webpack_preset')],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ['default', 'json', 'painless', 'xjson'].map(getWorkerConfig);
|
||||
|
|
|
@ -238,4 +238,39 @@ storiesOf('CodeEditor', module)
|
|||
text: 'Hover dialog example can be triggered by hovering over a word',
|
||||
},
|
||||
}
|
||||
)
|
||||
.add(
|
||||
'json support',
|
||||
() => (
|
||||
<div>
|
||||
<CodeEditor
|
||||
languageId="json"
|
||||
editorDidMount={(editor) => {
|
||||
monacoEditor.languages.json.jsonDefaults.setDiagnosticsOptions({
|
||||
validate: true,
|
||||
schemas: [
|
||||
{
|
||||
uri: editor.getModel()?.uri.toString() ?? '',
|
||||
fileMatch: ['*'],
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
version: {
|
||||
enum: ['v1', 'v2'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}}
|
||||
height={250}
|
||||
value="{}"
|
||||
onChange={action('onChange')}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
info: { text: 'JSON language support' },
|
||||
}
|
||||
);
|
||||
|
|
|
@ -34,23 +34,36 @@ const simpleLogLang: monaco.languages.IMonarchLanguage = {
|
|||
},
|
||||
};
|
||||
|
||||
monaco.languages.register({ id: 'loglang' });
|
||||
monaco.languages.setMonarchTokensProvider('loglang', simpleLogLang);
|
||||
|
||||
const logs = `
|
||||
[Sun Mar 7 20:54:27 2004] [notice] [client xx.xx.xx.xx] This is a notice!
|
||||
[Sun Mar 7 20:58:27 2004] [info] [client xx.xx.xx.xx] (104)Connection reset by peer: client stopped connection before send body completed
|
||||
[Sun Mar 7 21:16:17 2004] [error] [client xx.xx.xx.xx] File does not exist: /home/httpd/twiki/view/Main/WebHome
|
||||
`;
|
||||
|
||||
class ResizeObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
describe('<CodeEditor />', () => {
|
||||
window.ResizeObserver = ResizeObserver;
|
||||
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() {}
|
||||
};
|
||||
|
||||
monaco.languages.register({ id: 'loglang' });
|
||||
monaco.languages.setMonarchTokensProvider('loglang', simpleLogLang);
|
||||
});
|
||||
|
||||
test('is rendered', () => {
|
||||
const component = mountWithIntl(
|
||||
|
|
|
@ -115,6 +115,8 @@ const expressionsLanguage: ExpressionsLanguage = {
|
|||
export function registerExpressionsLanguage(functions: ExpressionFunction[]) {
|
||||
expressionsLanguage.keywords = functions.map((fn) => fn.name);
|
||||
expressionsLanguage.deprecated = functions.filter((fn) => fn.deprecated).map((fn) => fn.name);
|
||||
monaco.languages.register({ id: EXPRESSIONS_LANGUAGE_ID });
|
||||
monaco.languages.setMonarchTokensProvider(EXPRESSIONS_LANGUAGE_ID, expressionsLanguage);
|
||||
monaco.languages.onLanguage(EXPRESSIONS_LANGUAGE_ID, () => {
|
||||
monaco.languages.register({ id: EXPRESSIONS_LANGUAGE_ID });
|
||||
monaco.languages.setMonarchTokensProvider(EXPRESSIONS_LANGUAGE_ID, expressionsLanguage);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -62,5 +62,7 @@ export const lexerRules = {
|
|||
},
|
||||
} as monaco.languages.IMonarchLanguage;
|
||||
|
||||
monaco.languages.setMonarchTokensProvider(LANGUAGE_ID, lexerRules);
|
||||
monaco.languages.setLanguageConfiguration(LANGUAGE_ID, languageConfiguration);
|
||||
monaco.languages.onLanguage(LANGUAGE_ID, () => {
|
||||
monaco.languages.setMonarchTokensProvider(LANGUAGE_ID, lexerRules);
|
||||
monaco.languages.setLanguageConfiguration(LANGUAGE_ID, languageConfiguration);
|
||||
});
|
||||
|
|
|
@ -19366,10 +19366,10 @@ moment-timezone@*, moment-timezone@^0.5.34:
|
|||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||
|
||||
monaco-editor@*, monaco-editor@^0.22.3:
|
||||
version "0.22.3"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.22.3.tgz#69b42451d3116c6c08d9b8e052007ff891fd85d7"
|
||||
integrity sha512-RM559z2CJbczZ3k2b+ouacMINkAYWwRit4/vs0g2X/lkYefDiu0k2GmgWjAuiIpQi+AqASPOKvXNmYc8KUSvVQ==
|
||||
monaco-editor@*, monaco-editor@^0.24.0:
|
||||
version "0.24.0"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.24.0.tgz#990b55096bcc95d08d8d28e55264c6eb17707269"
|
||||
integrity sha512-o1f0Lz6ABFNTtnEqqqvlY9qzNx24rQZx1RgYNQ8SkWkE+Ka63keHH/RqxQ4QhN4fs/UYOnvAtEUZsPrzccH++A==
|
||||
|
||||
monitor-event-loop-delay@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue