From d0569a6816a735affecef0aa343e51db301b1915 Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 20 May 2021 02:06:57 +0800 Subject: [PATCH] ui: problem_files: yaml editor with schema --- .gitignore | 1 + packages/ui-default/build/config/webpack.js | 12 ++++- .../ui-default/components/editor/index.js | 13 +++-- .../ui-default/components/monaco/index.js | 49 +++++++++++++++++-- packages/ui-default/jsconfig.json | 2 - .../ui-default/pages/problem_files.page.js | 17 ++++++- 6 files changed, 83 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 9d8d0ce96..514e7104c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ packages/**/*.js !/install/jssh.d.ts ## ui-default !packages/ui-default/**/*.js +!examples/** packages/ui-default/public packages/ui-default/misc/.iconfont packages/ui-default/static/locale diff --git a/packages/ui-default/build/config/webpack.js b/packages/ui-default/build/config/webpack.js index 84a8543b2..7c81190f7 100644 --- a/packages/ui-default/build/config/webpack.js +++ b/packages/ui-default/build/config/webpack.js @@ -142,6 +142,7 @@ export default function (env = {}) { 'window.jQuery': 'jquery', katex: 'katex/dist/katex.js', React: 'react', + monaco: 'monaco-editor/esm/vs/editor/editor.api', }), new ExtractCssPlugin({ filename: '[name].css?[hash:10]', @@ -184,7 +185,16 @@ export default function (env = {}) { customInterpolateName: (url) => beautifyOutputUrl(url), }, }), - new MonacoWebpackPlugin({}), + new MonacoWebpackPlugin({ + customLanguages: [{ + label: 'yaml', + entry: require.resolve('@undefined-moe/monaco-yaml/lib/esm/monaco.contribution'), + worker: { + id: 'vs/language/yaml/yamlWorker', + entry: require.resolve('@undefined-moe/monaco-yaml/lib/esm/yaml.worker.js'), + }, + }], + }), new StaticManifestPlugin({ fileName: 'static-manifest.json', ignore: [ diff --git a/packages/ui-default/components/editor/index.js b/packages/ui-default/components/editor/index.js index e2e90559c..5c4e563b2 100644 --- a/packages/ui-default/components/editor/index.js +++ b/packages/ui-default/components/editor/index.js @@ -31,12 +31,12 @@ export default class Editor extends DOMAttachedObject { super($dom); this.options = options; if (UserContext.preferredEditorType === 'monaco') this.initMonaco(); - else if (options.language && options.langua !== 'markdown') this.initMonaco(); + else if (options.language && options.language !== 'markdown') this.initMonaco(); else this.initVditor(); } async initMonaco() { - const monaco = await import('monaco-editor/esm/vs/editor/editor.api'); + const { default: monaco } = await import('vj/components/monaco/index'); const { onChange, language = 'markdown', theme = UserContext.monacoTheme || 'vs-light', @@ -52,7 +52,14 @@ export default class Editor extends DOMAttachedObject { $dom.hide(); origin.parentElement.appendChild(ele); const value = this.options.value || $dom.val(); - this.model = typeof model === 'string' ? monaco.editor.createModel(value, language, monaco.Uri.parse(model)) : model; + // eslint-disable-next-line no-nested-ternary + this.model = typeof model === 'string' + ? monaco.editor.getModel(monaco.Uri.parse(model)) + ? monaco.editor.getModel(monaco.Uri.parse(model)) + : monaco.editor.createModel(value, language, monaco.Uri.parse(model)) + : model; + this.model.setValue(value); + this.model.updateOptions({ language }); const cfg = { theme, lineNumbers: true, diff --git a/packages/ui-default/components/monaco/index.js b/packages/ui-default/components/monaco/index.js index bfcdb055c..026027726 100644 --- a/packages/ui-default/components/monaco/index.js +++ b/packages/ui-default/components/monaco/index.js @@ -20,12 +20,55 @@ monaco.languages.yaml.yamlDefaults.setDiagnosticsOptions({ enableSchemaRequest: true, hover: true, completion: true, + format: true, schemas: [ { - uri: '/manage/system-schema.json', - fileMatch: ['hydro://system/config.yaml'], + uri: 'https://hydro.js.org/schema/problemConfig.json', + fileMatch: ['hydro://problem/file/config.yaml'], + schema: { + type: 'object', + def: { + cases: { type: 'array', items: { $ref: '#/def/case' } }, + case: { + type: 'object', + properties: { + input: { type: 'string' }, + output: { type: 'string' }, + }, + required: ['input'], + }, + subtask: { + description: 'Subtask Info', + type: 'object', + properties: { + time: { $ref: '#/def/time' }, + memory: { $ref: '#/def/memory' }, + score: { $ref: '#/def/score', description: 'score' }, + cases: { $ref: '#/def/cases' }, + if: { type: 'array', items: { type: 'integer' } }, + }, + }, + time: { type: 'string', pattern: '^([0-9]+(?:\\.[0-9]*)?)([mu]?)s?$' }, + memory: { type: 'string', pattern: '^([0-9]+(?:\\.[0-9]*)?)([kmg])b?$' }, + score: { type: 'integer', maximum: 100, minimum: 1 }, + }, + properties: { + type: { enum: ['default', 'interactive', 'submit_answer'] }, + checker_type: { enum: ['default', 'lemon', 'syzoj', 'testlib', 'strict', 'qduoj'] }, + checker: { type: 'string', pattern: '\\.' }, + interactor: { type: 'string', pattern: '\\.' }, + user_extra_files: { type: 'array', items: { type: 'string' } }, + judge_extra_files: { type: 'array', items: { type: 'string' } }, + cases: { $ref: '#/def/cases' }, + subtasks: { type: 'array', items: { $ref: '#/def/subtask' } }, + outputs: { type: 'array' }, + filename: { type: 'string' }, + time: { $ref: '#/def/time' }, + memory: { $ref: '#/def/memory' }, + score: { $ref: '#/def/score' }, + }, + }, }, - ...window.Context.schemas, ], }); diff --git a/packages/ui-default/jsconfig.json b/packages/ui-default/jsconfig.json index 3d07a47c8..83a9416eb 100644 --- a/packages/ui-default/jsconfig.json +++ b/packages/ui-default/jsconfig.json @@ -1,8 +1,6 @@ { "exclude": [ - "./node_modules", "./public", - "../../node_modules" ], "compilerOptions": { "allowSyntheticDefaultImports": true, diff --git a/packages/ui-default/pages/problem_files.page.js b/packages/ui-default/pages/problem_files.page.js index 0ace60d94..6a8deae67 100644 --- a/packages/ui-default/pages/problem_files.page.js +++ b/packages/ui-default/pages/problem_files.page.js @@ -213,7 +213,6 @@ const page = new NamedPage('problem_files', () => { } async function startEdit(filename, value) { - console.log(filename, value); const { default: Editor } = await import('vj/components/editor/index'); const promise = new ActionDialog({ $body: tpl` @@ -223,7 +222,21 @@ const page = new NamedPage('problem_files', () => { width: `${window.innerWidth - 200}px`, height: `${window.innerHeight - 100}px`, }).open(); - const editor = new Editor($('[name="fileContent"]'), { value, autoResize: false, autoLayout: false }); + const language = { + yaml: 'yaml', + yml: 'yaml', + txt: 'plain', + in: 'plain', + out: 'plain', + ans: 'plain', + }[filename.split('.').pop()]; + const editor = new Editor($('[name="fileContent"]'), { + value, + autoResize: false, + autoLayout: false, + language, + model: `hydro://problem/file/${filename}`, + }); const action = await promise; value = editor.value(); editor.destory();