Skip to content

Commit 32650bd

Browse files
committed
Refactor demo code
1 parent f137b9f commit 32650bd

File tree

4 files changed

+201
-33
lines changed

4 files changed

+201
-33
lines changed

explorer-v2/src/lib/ESLintEditor.svelte

+105-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script>
22
import MonacoEditor from './MonacoEditor.svelte';
3-
import { monacoEditorLoad } from './scripts/monaco-loader';
3+
import { loadMonacoEditor } from './scripts/monaco-loader';
44
import { createEventDispatcher, onMount } from 'svelte';
55
66
const dispatch = createEventDispatcher();
@@ -14,6 +14,8 @@
1414
let leftMarkers = [];
1515
let rightMarkers = [];
1616
17+
let messageMap = new Map();
18+
1719
$: {
1820
lint(linter, code, config, options);
1921
}
@@ -23,6 +25,7 @@
2325
});
2426
2527
async function lint(linter, code, config, options) {
28+
messageMap.clear();
2629
/* eslint-disable no-param-reassign -- ignore */
2730
linter = await linter;
2831
if (!linter) {
@@ -49,13 +52,13 @@
4952
fixedMessages: fixResult.messages
5053
});
5154
52-
leftMarkers = await Promise.all(messages.map(messageToMarker));
53-
rightMarkers = await Promise.all(fixResult.messages.map(messageToMarker));
55+
leftMarkers = await Promise.all(messages.map((m) => messageToMarker(m, messageMap)));
56+
rightMarkers = await Promise.all(fixResult.messages.map((m) => messageToMarker(m)));
5457
}
5558
5659
/** message to marker */
57-
async function messageToMarker(message) {
58-
const monaco = await monacoEditorLoad;
60+
async function messageToMarker(message, messageMap) {
61+
const monaco = await loadMonacoEditor();
5962
const rule = message.ruleId && linter.getRules().get(message.ruleId);
6063
const docUrl = rule && rule.meta && rule.meta.docs && rule.meta.docs.url;
6164
const startLineNumber = ensurePositiveInt(message.line, 1);
@@ -65,7 +68,7 @@
6568
const code = docUrl
6669
? { value: message.ruleId, link: docUrl, target: docUrl }
6770
: message.ruleId || 'FATAL';
68-
return {
71+
const marker = {
6972
code,
7073
severity: monaco.MarkerSeverity.Error,
7174
source: 'ESLint',
@@ -75,6 +78,10 @@
7578
endLineNumber,
7679
endColumn
7780
};
81+
if (messageMap) {
82+
messageMap.set(computeKey(marker), message);
83+
}
84+
return marker;
7885
}
7986
8087
/**
@@ -86,6 +93,97 @@
8693
function ensurePositiveInt(value, defaultValue) {
8794
return Math.max(1, (value !== undefined ? value : defaultValue) | 0);
8895
}
96+
97+
function provideCodeActions(model, _range, context) {
98+
if (context.only !== 'quickfix') {
99+
return {
100+
actions: [],
101+
dispose() {
102+
/* nop */
103+
}
104+
};
105+
}
106+
107+
const actions = [];
108+
for (const marker of context.markers) {
109+
const message = messageMap.get(computeKey(marker));
110+
if (!message) {
111+
continue;
112+
}
113+
if (message.fix) {
114+
actions.push(
115+
createQuickfixCodeAction(`Fix this ${message.ruleId} problem`, marker, model, message.fix)
116+
);
117+
}
118+
if (message.suggestions) {
119+
for (const suggestion of message.suggestions) {
120+
actions.push(
121+
createQuickfixCodeAction(
122+
`${suggestion.desc} (${message.ruleId})`,
123+
marker,
124+
model,
125+
suggestion.fix
126+
)
127+
);
128+
}
129+
}
130+
}
131+
132+
return {
133+
actions,
134+
dispose() {
135+
/* nop */
136+
}
137+
};
138+
}
139+
140+
/**
141+
* Computes the key string from the given marker.
142+
* @param {import('monaco-editor').editor.IMarkerData} marker marker
143+
* @returns {string} the key string
144+
*/
145+
function computeKey(marker) {
146+
const code =
147+
(typeof marker.code === 'string' ? marker.code : marker.code && marker.code.value) || '';
148+
return `[${marker.startLineNumber},${marker.startColumn},${marker.endLineNumber},${marker.endColumn}]-${code}`;
149+
}
150+
/**
151+
* Create quickfix code action.
152+
* @param {string} title title
153+
* @param {import('monaco-editor').editor.IMarkerData} marker marker
154+
* @param {import('monaco-editor').editor.ITextModel} model model
155+
* @param { { range: [number, number], text: string } } fix fix data
156+
* @returns {import('monaco-editor').languages.CodeAction} CodeAction
157+
*/
158+
function createQuickfixCodeAction(title, marker, model, fix) {
159+
const start = model.getPositionAt(fix.range[0]);
160+
const end = model.getPositionAt(fix.range[1]);
161+
/**
162+
* @type {import('monaco-editor').IRange}
163+
*/
164+
const editRange = {
165+
startLineNumber: start.lineNumber,
166+
startColumn: start.column,
167+
endLineNumber: end.lineNumber,
168+
endColumn: end.column
169+
};
170+
return {
171+
title,
172+
diagnostics: [marker],
173+
kind: 'quickfix',
174+
edit: {
175+
edits: [
176+
{
177+
resource: model.uri,
178+
edit: {
179+
range: editRange,
180+
text: fix.text
181+
}
182+
}
183+
]
184+
}
185+
};
186+
}
89187
</script>
90188

91189
<MonacoEditor
@@ -95,6 +193,7 @@
95193
diffEditor
96194
markers={leftMarkers}
97195
{rightMarkers}
196+
{provideCodeActions}
98197
/>
99198

100199
<style></style>

explorer-v2/src/lib/ESLintPlayground.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@
111111
See result of
112112
<a href="https://github.com/sveltejs/eslint-plugin-svelte3">eslint-plugin-svelte3</a>.
113113
</label>
114-
<template v-if="useEslintPluginSvelte3">
114+
{#if useEslintPluginSvelte3}
115115
<span style="color: red">svelte-eslint-parser is not used.</span>
116-
</template>
116+
{/if}
117117
<span style="margin-left: 16px">{time}</span>
118118
</div>
119119
<div class="playground-content">

explorer-v2/src/lib/MonacoEditor.svelte

+43-5
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,24 @@
33
44
const dispatch = createEventDispatcher();
55
6-
import { monacoEditorLoad } from './scripts/monaco-loader';
6+
import { loadMonacoEditor } from './scripts/monaco-loader';
77
export let code = '';
88
export let rightCode = '';
9-
export let language = 'json';
9+
export let language = 'javascript';
1010
export let readOnly = false;
1111
export let diffEditor = false;
1212
export let markers = [];
1313
export let rightMarkers = [];
14+
export let provideCodeActions = null;
1415
15-
let rootElement, editor, setLeftValue, setRightValue, setLeftMarkers, setRightMarkers;
16+
let rootElement,
17+
editor,
18+
setLeftValue,
19+
setRightValue,
20+
setLeftMarkers,
21+
setRightMarkers,
22+
getLeftEditor,
23+
codeActionProviderDisposable;
1624
$: {
1725
if (setLeftValue) {
1826
setLeftValue(code);
@@ -33,9 +41,30 @@
3341
setRightMarkers(rightMarkers);
3442
}
3543
}
44+
$: {
45+
disposeCodeActionProvider();
46+
if (provideCodeActions) {
47+
loadMonacoEditor().then((monaco) => {
48+
codeActionProviderDisposable = monaco.languages.registerCodeActionProvider(language, {
49+
provideCodeActions(model, range, context) {
50+
const editor = getLeftEditor?.();
51+
if (editor?.getModel().url !== model.url) {
52+
return {
53+
actions: [],
54+
dispose() {
55+
/* nop */
56+
}
57+
};
58+
}
59+
return provideCodeActions(model, range, context);
60+
}
61+
});
62+
});
63+
}
64+
}
3665
3766
onMount(async () => {
38-
const monaco = await monacoEditorLoad;
67+
const monaco = await loadMonacoEditor();
3968
const options = {
4069
value: code,
4170
readOnly,
@@ -88,6 +117,7 @@
88117
setRightMarkers = (markers) => {
89118
updateMarkers(rightEditor, markers);
90119
};
120+
getLeftEditor = () => leftEditor;
91121
92122
setLeftMarkers(markers);
93123
setRightMarkers(rightMarkers);
@@ -118,11 +148,13 @@
118148
setRightMarkers = () => {
119149
/* noop */
120150
};
151+
getLeftEditor = () => editor;
121152
122153
setLeftMarkers(markers);
123154
}
124155
});
125156
onDestroy(() => {
157+
disposeCodeActionProvider();
126158
dispose(editor);
127159
// rootElement.innerHTML = ""
128160
editor = null;
@@ -140,7 +172,7 @@
140172
}
141173
}
142174
async function updateMarkers(editor, markers) {
143-
const monaco = await monacoEditorLoad;
175+
const monaco = await loadMonacoEditor();
144176
const model = editor.getModel();
145177
const id = editor.getId();
146178
monaco.editor.setModelMarkers(model, id, JSON.parse(JSON.stringify(markers)));
@@ -168,6 +200,12 @@
168200
x.dispose();
169201
}
170202
}
203+
204+
function disposeCodeActionProvider() {
205+
if (codeActionProviderDisposable) {
206+
codeActionProviderDisposable.dispose();
207+
}
208+
}
171209
</script>
172210
173211
<div bind:this={rootElement} class="root" />
+51-20
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,55 @@
1-
if (typeof window !== 'undefined') {
2-
const monacoScript = Array.from(document.head.querySelectorAll('script')).find(
3-
(script) => script.src && script.src.includes('monaco')
4-
);
5-
window.require.config({
6-
paths: {
7-
vs: monacoScript.src.replace(/\/vs\/.*$/u, '/vs')
8-
},
9-
'vs/nls': {
10-
availableLanguages: {
11-
'*': 'ja'
1+
async function setupMonaco() {
2+
if (typeof window !== 'undefined') {
3+
const monacoScript =
4+
Array.from(document.head.querySelectorAll('script')).find(
5+
(script) => script.src && script.src.includes('monaco') && script.src.includes('vs/loader')
6+
) || (await appendMonacoEditorScript());
7+
window.require.config({
8+
paths: {
9+
vs: monacoScript.src.replace(/\/vs\/.*$/u, '/vs')
10+
},
11+
'vs/nls': {
12+
availableLanguages: {
13+
'*': 'ja'
14+
}
1215
}
13-
}
16+
});
17+
}
18+
}
19+
20+
function appendMonacoEditorScript() {
21+
return new Promise((resolve) => {
22+
const script = document.createElement('script');
23+
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/loader.min.js';
24+
script.onload = () => {
25+
script.onload = null;
26+
27+
watch();
28+
29+
function watch() {
30+
if (window.require) {
31+
resolve(script);
32+
return;
33+
}
34+
setTimeout(watch, 200);
35+
}
36+
};
37+
document.head.append(script);
1438
});
1539
}
16-
const editorLoaded = new Promise((resolve) => {
17-
if (typeof window !== 'undefined')
18-
// eslint-disable-next-line node/no-missing-require -- ignore
19-
window.require(['vs/editor/editor.main'], (r) => {
20-
resolve(r);
21-
});
22-
});
2340

24-
export const monacoEditorLoad = editorLoaded;
41+
let setupedMonaco = null;
42+
let editorLoaded = null;
43+
44+
export async function loadMonacoEditor() {
45+
await (setupedMonaco || (setupedMonaco = setupMonaco()));
46+
return (
47+
editorLoaded ||
48+
(editorLoaded = new Promise((resolve) => {
49+
// eslint-disable-next-line node/no-missing-require -- ignore
50+
window.require(['vs/editor/editor.main'], (r) => {
51+
resolve(r);
52+
});
53+
}))
54+
);
55+
}

0 commit comments

Comments
 (0)