Commit f56761e0 authored by Himanshu Kapoor's avatar Himanshu Kapoor Committed by Illya Klymov

Add support for linting based on schemas in WebIDE

This allows us to lint our .gitlab-ci.yml file and other yaml
files in general
parent 1c9315e3
...@@ -8,9 +8,10 @@ import ModelManager from './common/model_manager'; ...@@ -8,9 +8,10 @@ import ModelManager from './common/model_manager';
import { editorOptions, defaultEditorOptions, defaultDiffEditorOptions } from './editor_options'; import { editorOptions, defaultEditorOptions, defaultDiffEditorOptions } from './editor_options';
import { themes } from './themes'; import { themes } from './themes';
import languages from './languages'; import languages from './languages';
import schemas from './schemas';
import keymap from './keymap.json'; import keymap from './keymap.json';
import { clearDomElement } from '~/editor/utils'; import { clearDomElement } from '~/editor/utils';
import { registerLanguages } from '../utils'; import { registerLanguages, registerSchemas } from '../utils';
function setupThemes() { function setupThemes() {
themes.forEach(theme => { themes.forEach(theme => {
...@@ -44,6 +45,7 @@ export default class Editor { ...@@ -44,6 +45,7 @@ export default class Editor {
setupThemes(); setupThemes();
registerLanguages(...languages); registerLanguages(...languages);
registerSchemas(...schemas);
this.debouncedUpdate = debounce(() => { this.debouncedUpdate = debounce(() => {
this.updateDimensions(); this.updateDimensions();
......
import json from './json';
import yaml from './yaml';
export default [json, yaml];
export default {
language: 'json',
options: {
validate: true,
enableSchemaRequest: true,
schemas: [],
},
};
export default {
uri: 'https://json.schemastore.org/gitlab-ci',
fileMatch: ['*.gitlab-ci.yml'],
};
import gitlabCi from './gitlab_ci';
export default {
language: 'yaml',
options: {
validate: true,
enableSchemaRequest: true,
hover: true,
completion: true,
schemas: [gitlabCi],
},
};
...@@ -66,7 +66,7 @@ export const trimPathComponents = path => ...@@ -66,7 +66,7 @@ export const trimPathComponents = path =>
.join('/'); .join('/');
export function registerLanguages(def, ...defs) { export function registerLanguages(def, ...defs) {
if (defs.length) defs.forEach(lang => registerLanguages(lang)); defs.forEach(lang => registerLanguages(lang));
const languageId = def.id; const languageId = def.id;
...@@ -75,6 +75,19 @@ export function registerLanguages(def, ...defs) { ...@@ -75,6 +75,19 @@ export function registerLanguages(def, ...defs) {
languages.setLanguageConfiguration(languageId, def.conf); languages.setLanguageConfiguration(languageId, def.conf);
} }
export function registerSchemas({ language, options }, ...schemas) {
schemas.forEach(schema => registerSchemas(schema));
const defaults = {
json: languages.json.jsonDefaults,
yaml: languages.yaml.yamlDefaults,
};
if (defaults[language]) {
defaults[language].setDiagnosticsOptions(options);
}
}
export const otherSide = side => (side === SIDE_RIGHT ? SIDE_LEFT : SIDE_RIGHT); export const otherSide = side => (side === SIDE_RIGHT ? SIDE_LEFT : SIDE_RIGHT);
export function trimTrailingWhitespace(content) { export function trimTrailingWhitespace(content) {
......
---
title: Add support for linting based on schemas in WebIDE
merge_request: 35838
author:
type: added
const { languagesArr } = require('monaco-editor-webpack-plugin/out/languages');
// monaco-yaml library doesn't play so well with monaco-editor-webpack-plugin
// so the only way to include its workers is by patching the list of languages
// in monaco-editor-webpack-plugin and adding support for yaml workers. This is
// a known issue in the library and this workaround was suggested here:
// https://github.com/pengx17/monaco-yaml/issues/20
const yamlLang = languagesArr.find(t => t.label === 'yaml');
yamlLang.entry = [yamlLang.entry, '../../monaco-yaml/esm/monaco.contribution'];
yamlLang.worker = {
id: 'vs/language/yaml/yamlWorker',
entry: '../../monaco-yaml/esm/yaml.worker.js',
};
module.exports = require('monaco-editor-webpack-plugin');
...@@ -5,7 +5,7 @@ const webpack = require('webpack'); ...@@ -5,7 +5,7 @@ const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin'); const VueLoaderPlugin = require('vue-loader/lib/plugin');
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin; const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
const CompressionPlugin = require('compression-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); const MonacoWebpackPlugin = require('./plugins/monaco_webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const vendorDllHash = require('./helpers/vendor_dll_hash'); const vendorDllHash = require('./helpers/vendor_dll_hash');
...@@ -241,7 +241,7 @@ module.exports = { ...@@ -241,7 +241,7 @@ module.exports = {
}, },
{ {
test: /\.(eot|ttf|woff|woff2)$/, test: /\.(eot|ttf|woff|woff2)$/,
include: /node_modules\/katex\/dist\/fonts/, include: /node_modules\/(katex\/dist\/fonts|monaco-editor)/,
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[contenthash:8].[ext]', name: '[name].[contenthash:8].[ext]',
......
...@@ -82,7 +82,9 @@ module.exports = path => { ...@@ -82,7 +82,9 @@ module.exports = path => {
'^.+\\.js$': 'babel-jest', '^.+\\.js$': 'babel-jest',
'^.+\\.vue$': 'vue-jest', '^.+\\.vue$': 'vue-jest',
}, },
transformIgnorePatterns: ['node_modules/(?!(@gitlab/ui|bootstrap-vue|three|monaco-editor)/)'], transformIgnorePatterns: [
'node_modules/(?!(@gitlab/ui|bootstrap-vue|three|monaco-editor|monaco-yaml)/)',
],
timers: 'fake', timers: 'fake',
testEnvironment: '<rootDir>/spec/frontend/environment.js', testEnvironment: '<rootDir>/spec/frontend/environment.js',
testEnvironmentOptions: { testEnvironmentOptions: {
......
...@@ -97,6 +97,7 @@ ...@@ -97,6 +97,7 @@
"jquery.caret": "^0.3.1", "jquery.caret": "^0.3.1",
"jquery.waitforimages": "^2.2.0", "jquery.waitforimages": "^2.2.0",
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"js-yaml": "^3.13.1",
"jszip": "^3.1.3", "jszip": "^3.1.3",
"jszip-utils": "^0.0.2", "jszip-utils": "^0.0.2",
"katex": "^0.10.0", "katex": "^0.10.0",
...@@ -105,8 +106,9 @@ ...@@ -105,8 +106,9 @@
"mermaid": "^8.5.2", "mermaid": "^8.5.2",
"mersenne-twister": "1.1.0", "mersenne-twister": "1.1.0",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"monaco-editor": "^0.18.1", "monaco-editor": "^0.20.0",
"monaco-editor-webpack-plugin": "^1.7.0", "monaco-editor-webpack-plugin": "^1.9.0",
"monaco-yaml": "^2.4.0",
"mousetrap": "^1.4.6", "mousetrap": "^1.4.6",
"pdfjs-dist": "^2.0.943", "pdfjs-dist": "^2.0.943",
"pikaday": "^1.8.0", "pikaday": "^1.8.0",
...@@ -220,7 +222,7 @@ ...@@ -220,7 +222,7 @@
}, },
"resolutions": { "resolutions": {
"chokidar": "^3.4.0", "chokidar": "^3.4.0",
"monaco-editor": "0.18.1", "monaco-editor": "0.20.0",
"vue-jest/ts-jest": "24.0.0" "vue-jest/ts-jest": "24.0.0"
}, },
"engines": { "engines": {
......
...@@ -8,9 +8,11 @@ import 'monaco-editor/esm/vs/language/css/monaco.contribution'; ...@@ -8,9 +8,11 @@ import 'monaco-editor/esm/vs/language/css/monaco.contribution';
import 'monaco-editor/esm/vs/language/json/monaco.contribution'; import 'monaco-editor/esm/vs/language/json/monaco.contribution';
import 'monaco-editor/esm/vs/language/html/monaco.contribution'; import 'monaco-editor/esm/vs/language/html/monaco.contribution';
import 'monaco-editor/esm/vs/basic-languages/monaco.contribution'; import 'monaco-editor/esm/vs/basic-languages/monaco.contribution';
import 'monaco-yaml/esm/monaco.contribution';
// This language starts trying to spin up web workers which obviously breaks in Jest environment // This language starts trying to spin up web workers which obviously breaks in Jest environment
jest.mock('monaco-editor/esm/vs/language/typescript/tsMode'); jest.mock('monaco-editor/esm/vs/language/typescript/tsMode');
jest.mock('monaco-yaml/esm/yamlMode');
export * from 'monaco-editor/esm/vs/editor/editor.api'; export * from 'monaco-editor/esm/vs/editor/editor.api';
export default global.monaco; export default global.monaco;
...@@ -637,6 +637,8 @@ describe('RepoEditor', () => { ...@@ -637,6 +637,8 @@ describe('RepoEditor', () => {
// set cursor to line 2, column 1 // set cursor to line 2, column 1
vm.editor.instance.setSelection(new Range(2, 1, 2, 1)); vm.editor.instance.setSelection(new Range(2, 1, 2, 1));
vm.editor.instance.focus(); vm.editor.instance.focus();
jest.spyOn(vm.editor.instance, 'hasTextFocus').mockReturnValue(true);
}); });
}); });
......
...@@ -199,6 +199,14 @@ describe('Multi-file editor library', () => { ...@@ -199,6 +199,14 @@ describe('Multi-file editor library', () => {
}); });
}); });
describe('schemas', () => {
it('registers custom schemas defined with Monaco', () => {
expect(monacoLanguages.yaml.yamlDefaults.diagnosticsOptions).toMatchObject({
schemas: [{ fileMatch: ['*.gitlab-ci.yml'] }],
});
});
});
describe('replaceSelectedText', () => { describe('replaceSelectedText', () => {
let model; let model;
let editor; let editor;
......
import { import {
isTextFile, isTextFile,
registerLanguages, registerLanguages,
registerSchemas,
trimPathComponents, trimPathComponents,
insertFinalNewline, insertFinalNewline,
trimTrailingWhitespace, trimTrailingWhitespace,
...@@ -158,6 +159,57 @@ describe('WebIDE utils', () => { ...@@ -158,6 +159,57 @@ describe('WebIDE utils', () => {
}); });
}); });
describe('registerSchemas', () => {
let options;
beforeEach(() => {
options = {
validate: true,
enableSchemaRequest: true,
hover: true,
completion: true,
schemas: [
{
uri: 'http://myserver/foo-schema.json',
fileMatch: ['*'],
schema: {
id: 'http://myserver/foo-schema.json',
type: 'object',
properties: {
p1: { enum: ['v1', 'v2'] },
p2: { $ref: 'http://myserver/bar-schema.json' },
},
},
},
{
uri: 'http://myserver/bar-schema.json',
schema: {
id: 'http://myserver/bar-schema.json',
type: 'object',
properties: { q1: { enum: ['x1', 'x2'] } },
},
},
],
};
jest.spyOn(languages.json.jsonDefaults, 'setDiagnosticsOptions');
jest.spyOn(languages.yaml.yamlDefaults, 'setDiagnosticsOptions');
});
it.each`
language | defaultsObj
${'json'} | ${languages.json.jsonDefaults}
${'yaml'} | ${languages.yaml.yamlDefaults}
`(
'registers the given schemas with monaco for lang: $language',
({ language, defaultsObj }) => {
registerSchemas({ language, options });
expect(defaultsObj.setDiagnosticsOptions).toHaveBeenCalledWith(options);
},
);
});
describe('trimTrailingWhitespace', () => { describe('trimTrailingWhitespace', () => {
it.each` it.each`
input | output input | output
......
...@@ -1171,11 +1171,6 @@ ...@@ -1171,11 +1171,6 @@
dependencies: dependencies:
"@toast-ui/editor" "^2.2.0" "@toast-ui/editor" "^2.2.0"
"@types/anymatch@*":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.0.tgz#d1d55958d1fccc5527d4aba29fc9c4b942f563ff"
integrity sha512-7WcbyctkE8GTzogDb0ulRAEw7v8oIS54ft9mQTU7PfM0hp5e+8kpa+HeQ7IQrFbKtJXBKcZ4bh+Em9dTw5L6AQ==
"@types/babel__core@^7.1.0": "@types/babel__core@^7.1.0":
version "7.1.2" version "7.1.2"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f"
...@@ -1285,11 +1280,6 @@ ...@@ -1285,11 +1280,6 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
"@types/tapable@*":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370"
integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==
"@types/tern@*": "@types/tern@*":
version "0.23.3" version "0.23.3"
resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.3.tgz#4b54538f04a88c9ff79de1f6f94f575a7f339460" resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.3.tgz#4b54538f04a88c9ff79de1f6f94f575a7f339460"
...@@ -1297,13 +1287,6 @@ ...@@ -1297,13 +1287,6 @@
dependencies: dependencies:
"@types/estree" "*" "@types/estree" "*"
"@types/uglify-js@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082"
integrity sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ==
dependencies:
source-map "^0.6.1"
"@types/unist@*", "@types/unist@^2.0.0": "@types/unist@*", "@types/unist@^2.0.0":
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
...@@ -1326,17 +1309,6 @@ ...@@ -1326,17 +1309,6 @@
"@types/unist" "*" "@types/unist" "*"
"@types/vfile-message" "*" "@types/vfile-message" "*"
"@types/webpack@^4.4.19":
version "4.4.23"
resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.23.tgz#059d6f4598cfd65ddee0e2db38317ef989696712"
integrity sha512-WswyG+2mRg0ul/ytPpCSWo+kOlVVPW/fKCBEVwqmPVC/2ffWEwhsCEQgnFbWDf8EWId2qGcpL623EjLfNTRk9A==
dependencies:
"@types/anymatch" "*"
"@types/node" "*"
"@types/tapable" "*"
"@types/uglify-js" "*"
source-map "^0.6.0"
"@types/yargs-parser@*": "@types/yargs-parser@*":
version "15.0.0" version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
...@@ -8252,17 +8224,24 @@ moment-mini@^2.22.1: ...@@ -8252,17 +8224,24 @@ moment-mini@^2.22.1:
resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.22.1.tgz#bc32d73e43a4505070be6b53494b17623183420d" resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.22.1.tgz#bc32d73e43a4505070be6b53494b17623183420d"
integrity sha512-OUCkHOz7ehtNMYuZjNciXUfwTuz8vmF1MTbAy59ebf+ZBYZO5/tZKuChVWCX+uDo+4idJBpGltNfV8st+HwsGw== integrity sha512-OUCkHOz7ehtNMYuZjNciXUfwTuz8vmF1MTbAy59ebf+ZBYZO5/tZKuChVWCX+uDo+4idJBpGltNfV8st+HwsGw==
monaco-editor-webpack-plugin@^1.7.0: monaco-editor-webpack-plugin@^1.9.0:
version "1.7.0" version "1.9.0"
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.7.0.tgz#920cbeecca25f15d70d568a7e11b0ba4daf1ae83" resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.9.0.tgz#5b547281b9f404057dc5d8c5722390df9ac90be6"
integrity sha512-oItymcnlL14Sjd7EF7q+CMhucfwR/2BxsqrXIBrWL6LQplFfAfV+grLEQRmVHeGSBZ/Gk9ptzfueXnWcoEcFuA== integrity sha512-tOiiToc94E1sb50BgZ8q8WK/bxus77SRrwCqIpAB5er3cpX78SULbEBY4YPOB8kDolOzKRt30WIHG/D6gz69Ww==
dependencies: dependencies:
"@types/webpack" "^4.4.19" loader-utils "^1.2.3"
monaco-editor@0.18.1, monaco-editor@^0.18.1: monaco-editor@0.20.0, monaco-editor@^0.20.0:
version "0.18.1" version "0.20.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.18.1.tgz#ced7c305a23109875feeaf395a504b91f6358cfc" resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea"
integrity sha512-fmL+RFZ2Hrezy+X/5ZczQW51LUmvzfcqOurnkCIRFTyjdVjzR7JvENzI6+VKBJzJdPh6EYL4RoWl92b2Hrk9fw== integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==
monaco-yaml@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/monaco-yaml/-/monaco-yaml-2.4.0.tgz#027307a231d809c416babf1cf89b4c1bb940e55d"
integrity sha512-ElUS6uBqEjA2/o2gLuNdnqWSAAQXh8ISr1kwlFErm3t5IXO74TNfS3gnjO6Kv9TXS7LImjGfgPAZei7o8zNTHw==
optionalDependencies:
prettier "^1.19.1"
mousetrap@^1.4.6: mousetrap@^1.4.6:
version "1.4.6" version "1.4.6"
...@@ -9387,11 +9366,16 @@ prettier@1.16.3: ...@@ -9387,11 +9366,16 @@ prettier@1.16.3:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d"
integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw== integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==
prettier@1.18.2, prettier@^1.18.2: prettier@1.18.2:
version "1.18.2" version "1.18.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw== integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
prettier@^1.18.2, prettier@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
pretty-format@^24.8.0: pretty-format@^24.8.0:
version "24.8.0" version "24.8.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment