Skip to content

Commit 76ab2b1

Browse files
authored
refactor: move dereference monaco action to apidom language plugin (#3981)
Action is now using worker to provide the dereference capability, and does not instantiate new language service instance.
1 parent e047b9a commit 76ab2b1

File tree

8 files changed

+113
-123
lines changed

8 files changed

+113
-123
lines changed

src/plugins/editor-monaco-language-apidom/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import makeAfterLoad from './after-load.js';
2+
import { getWorker } from './language/monaco.contribution.js';
23

34
const EditorMonacoLanguageApiDOMPlugin = (opts = {}) => {
45
const isCalledWithGetSystem = typeof opts.getSystem === 'function';
56
const options = isCalledWithGetSystem ? {} : opts;
67
const plugin = () => ({
78
afterLoad: makeAfterLoad(options),
9+
fn: {
10+
getApiDOMWorker: getWorker,
11+
},
812
});
913

1014
return isCalledWithGetSystem ? plugin(opts) : plugin;

src/plugins/editor-monaco-language-apidom/language/ApiDOMWorker.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,19 @@ export class ApiDOMWorker {
9090
return this._languageService.getSemanticTokensLegend();
9191
}
9292

93+
async doDeref(uri, dereferenceContext = {}) {
94+
const document = this._getTextDocument(uri);
95+
if (!document) {
96+
return [];
97+
}
98+
99+
return this._languageService.doDeref(document, dereferenceContext);
100+
}
101+
93102
_getTextDocument(uri) {
94-
const [model] = this._ctx.getMirrorModels();
95-
/**
96-
* When there are multiple files open, this will be an array
97-
* expect models: _lines[], _uri, _versionId
98-
* expect models.uri.toString() to be singular, e.g. inmemory://model/1
99-
* thus, before returning a TextDocument, we can optionally
100-
* validate that models.uri.toString() === uri
101-
* fyi, reference more complete example in cssWorker
102-
* https://github.com/microsoft/monaco-css/blob/master/src/cssWorker.ts
103-
* which we might want later to handle multiple URIs.
104-
*/
103+
const model = this._ctx.getMirrorModels().find((mm) => mm.uri.toString() === uri);
104+
105+
if (!model) return null;
105106

106107
return vscodeLanguageServerTextDocument.TextDocument.create(
107108
uri,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { getWorker } from '../apidom-mode.js';
2+
3+
const dereferenceActionDescriptor = {
4+
id: 'apidom-dereference',
5+
label: 'Resolve document',
6+
async run(editor) {
7+
const model = editor.getModel();
8+
const worker = await getWorker()();
9+
const dereferenced = await worker.doDeref(model.uri.toString(), {
10+
baseURI: globalThis.document.baseURI || globalThis.location.href,
11+
});
12+
13+
editor.setValue(dereferenced);
14+
},
15+
};
16+
17+
export default dereferenceActionDescriptor;

src/plugins/editor-monaco-language-apidom/language/adapters/DiagnosticsAdapter.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class DiagnosticsAdapter extends Adapter {
1616

1717
this.#diagnosticCollection = languages.createDiagnosticCollection(apidom.languageId);
1818

19-
const onModelAdd = (model) => {
19+
const onModelAdded = (model) => {
2020
if (model.getLanguageId() !== apidom.languageId) {
2121
return;
2222
}
@@ -50,7 +50,20 @@ class DiagnosticsAdapter extends Adapter {
5050
}
5151
};
5252

53-
this.#disposables.push(monaco.editor.onDidCreateModel(onModelAdd));
53+
const onModelLanguageChanged = (model) => {
54+
const key = model.uri.toString();
55+
const hasChangedToApiDOM = model.getLanguageId() === apidom.languageId;
56+
const isModelSubscribed = !!this.#listener[key];
57+
58+
if (!isModelSubscribed && hasChangedToApiDOM) {
59+
onModelAdded(model);
60+
} else if (isModelSubscribed && !hasChangedToApiDOM) {
61+
onModelRemoved(model);
62+
}
63+
};
64+
65+
this.#disposables.push(monaco.editor.onDidCreateModel(onModelAdded));
66+
this.#disposables.push(monaco.editor.onDidChangeModelLanguage(onModelLanguageChanged));
5467
this.#disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved));
5568
this.#disposables.push(this.#diagnosticCollection);
5669
}
@@ -64,7 +77,7 @@ class DiagnosticsAdapter extends Adapter {
6477
}
6578

6679
try {
67-
return await worker.doValidation(model.uri);
80+
return await worker.doValidation(model.uri.toString());
6881
} catch {
6982
return undefined;
7083
}
Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { languages } from 'vscode';
1+
import { languages as vscodeLanguages } from 'vscode';
22
import { createConverter as createCodeConverter } from 'vscode-languageclient/lib/common/codeConverter.js';
33
import { createConverter as createProtocolConverter } from 'vscode-languageclient/lib/common/protocolConverter.js';
44

@@ -34,31 +34,39 @@ export const getWorker = () => {
3434
const registerProviders = ({ languageId, providers, dependencies }) => {
3535
disposeAll(providers);
3636

37-
const { worker, codeConverter, protocolConverter, semanticTokensLegend } = dependencies;
37+
const { worker, codeConverter, protocolConverter } = dependencies;
3838
const args = [worker, codeConverter, protocolConverter];
3939

4040
providers.push(new DiagnosticsAdapter(...args));
41-
providers.push(languages.registerHoverProvider(languageId, new HoverAdapter(...args)));
41+
providers.push(vscodeLanguages.registerHoverProvider(languageId, new HoverAdapter(...args)));
4242
providers.push(
43-
languages.registerDocumentLinkProvider(languageId, new DocumentLinkAdapter(...args))
43+
vscodeLanguages.registerDocumentLinkProvider(languageId, new DocumentLinkAdapter(...args))
4444
);
4545
providers.push(
46-
languages.registerCompletionItemProvider(languageId, new CompletionItemsAdapter(...args))
46+
vscodeLanguages.registerCompletionItemProvider(languageId, new CompletionItemsAdapter(...args))
4747
);
4848
providers.push(
49-
languages.registerCodeActionsProvider(languageId, new CodeActionsAdapter(...args))
49+
vscodeLanguages.registerCodeActionsProvider(languageId, new CodeActionsAdapter(...args))
5050
);
5151
providers.push(
52-
languages.registerDocumentSymbolProvider(languageId, new DocumentSymbolsAdapter(...args))
52+
vscodeLanguages.registerDocumentSymbolProvider(languageId, new DocumentSymbolsAdapter(...args))
5353
);
5454
providers.push(
55-
languages.registerDocumentSemanticTokensProvider(
56-
languageId,
57-
new DocumentSemanticTokensAdapter(...args),
58-
semanticTokensLegend
59-
)
55+
vscodeLanguages.registerDefinitionProvider(languageId, new DefinitionAdapter(...args))
6056
);
61-
providers.push(languages.registerDefinitionProvider(languageId, new DefinitionAdapter(...args)));
57+
58+
(async () => {
59+
const workerService = await worker();
60+
const semanticTokensLegend = await workerService.getSemanticTokensLegend();
61+
62+
providers.push(
63+
vscodeLanguages.registerDocumentSemanticTokensProvider(
64+
languageId,
65+
new DocumentSemanticTokensAdapter(...args),
66+
semanticTokensLegend
67+
)
68+
);
69+
})();
6270

6371
return providers;
6472
};
@@ -68,30 +76,28 @@ export function setupMode(defaults) {
6876
const providers = [];
6977
const codeConverter = createCodeConverter();
7078
const protocolConverter = createProtocolConverter(undefined, true, true);
71-
const client = defaults.getModeConfiguration().client || new WorkerManager(defaults);
72-
const worker = async (...uris) => {
73-
return client.getLanguageServiceWorker(...uris);
74-
};
75-
const registeredProviders = registerProviders({
76-
languageId: defaults.getLanguageId(),
77-
providers,
78-
dependencies: {
79-
worker,
80-
codeConverter,
81-
protocolConverter,
82-
semanticTokensLegend: defaults.getModeConfiguration().semanticTokensLegend,
83-
},
84-
});
85-
86-
disposables.push(client);
87-
disposables.push(asDisposable(registeredProviders));
8879

80+
// setup apidom worker
81+
const client = new WorkerManager(defaults);
82+
const worker = async (...uris) => client.getLanguageServiceWorker(...uris);
8983
apidomWorker = worker;
9084
disposables.push({
9185
dispose() {
9286
apidomWorker = null;
9387
},
9488
});
89+
disposables.push(client);
90+
91+
// register apidom providers
92+
disposables.push(
93+
asDisposable(
94+
registerProviders({
95+
languageId: defaults.getLanguageId(),
96+
providers,
97+
dependencies: { worker, codeConverter, protocolConverter },
98+
})
99+
)
100+
);
95101

96102
return asDisposable(disposables);
97103
}

src/plugins/editor-monaco-language-apidom/language/monaco.contribution.js

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
import { languages as vscodeLanguages } from 'vscode';
21
import * as monaco from 'monaco-editor';
2+
import { languages as vscodeLanguages } from 'vscode';
33
import { ModesRegistry } from 'monaco-editor/esm/vs/editor/common/languages/modesRegistry.js';
44

55
import * as apidom from './apidom.js';
66
import { setupMode } from './apidom-mode.js';
7-
import WorkerManager from './WorkerManager.js';
7+
import dereferenceActionDescriptor from './actions/dereference.js';
88

99
export { getWorker } from './apidom-mode.js';
1010

11-
const modeConfigurationDefault = {
12-
semanticTokensLegend: {
13-
tokenModifiers: [],
14-
tokenTypes: [],
15-
},
16-
client: null,
17-
};
11+
const modeConfigurationDefault = {};
1812

1913
const workerOptionsDefault = {
2014
customWorkerPath: undefined,
@@ -73,6 +67,7 @@ export const isLanguageRegistered = () => {
7367
const lazyMonacoContribution = ({ createData }) => {
7468
const disposables = [];
7569

70+
// register apidom language
7671
disposables.push(
7772
/**
7873
* This code uses ModesRegistry API instead of monaco.languages API.
@@ -87,25 +82,36 @@ const lazyMonacoContribution = ({ createData }) => {
8782
disposables.push(vscodeLanguages.setLanguageConfiguration(apidom.languageId, apidom.conf));
8883
disposables.push(monaco.languages.setMonarchTokensProvider(apidom.languageId, apidom.language));
8984

85+
// setup apidom mode
9086
disposables.push(
91-
monaco.languages.onLanguage(apidom.languageId, async () => {
87+
monaco.editor.onDidCreateEditor(() => {
9288
const { customApiDOMWorkerPath: customWorkerPath, ...data } = createData;
9389
const defaults = new LanguageServiceDefaultsImpl({ customWorkerPath, data });
94-
const client = new WorkerManager(defaults);
95-
const worker = await client.getLanguageServiceWorker();
96-
const semanticTokensLegend = await worker.getSemanticTokensLegend();
97-
98-
defaults.getModeConfiguration().client = client;
99-
defaults.getModeConfiguration().semanticTokensLegend = semanticTokensLegend;
10090

10191
disposables.push(setupMode(defaults));
92+
})
93+
);
10294

103-
// disposing of all allocated disposables
95+
// setup custom actions
96+
disposables.push(
97+
monaco.editor.onDidCreateEditor((editor) => {
98+
disposables.push(
99+
monaco.editor.onDidCreateModel(() => {
100+
if (editor.getAction(dereferenceActionDescriptor.id)) return;
101+
102+
disposables.push(editor.addAction(dereferenceActionDescriptor));
103+
})
104+
);
105+
})
106+
);
107+
108+
// disposing of all allocated disposables
109+
disposables.push(
110+
monaco.editor.onDidCreateEditor((editor) => {
104111
disposables.push(
105-
monaco.editor.onWillDisposeModel((model) => {
106-
if (model.getLanguageId() === apidom.languageId) {
107-
disposables.forEach((disposable) => disposable.dispose());
108-
}
112+
editor.onDidDispose(() => {
113+
disposables.forEach((disposable) => disposable.dispose());
114+
disposables.length = 0;
109115
})
110116
);
111117
})

src/plugins/editor-monaco/components/MonacoEditor/MonacoEditor.jsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import noop from 'lodash/noop.js';
55

66
import seVsDarkTheme from '../../themes/se-vs-dark.js';
77
import seVsLightTheme from '../../themes/se-vs-light.js';
8-
import { dereference } from '../../utils/monaco-action-apidom-deref.js';
98
import { requestGetJsonPointerPosition } from '../../utils/monaco-jump-from-path-to-line.js';
109
import { useMount, useUpdate, useSmoothResize } from './hooks.js';
1110

@@ -69,8 +68,8 @@ const MonacoEditor = ({
6968
},
7069
renderWhitespace: true,
7170
});
72-
editorRef.current.getModel().updateOptions({ tabSize: 2 });
7371

72+
editorRef.current.getModel().updateOptions({ tabSize: 2 });
7473
setIsEditorReady(true);
7574
}, [value, language, theme, isReadOnly]);
7675

@@ -220,17 +219,6 @@ const MonacoEditor = ({
220219
}
221220
}, [isEditorReady, onChange]);
222221

223-
// set additional Monaco Editor actions
224-
useEffect(() => {
225-
if (isEditorReady) {
226-
editorRef.current.addAction({
227-
id: 'de-reference',
228-
label: 'resolve document',
229-
run: dereference,
230-
});
231-
}
232-
}, [isEditorReady]);
233-
234222
// allow editor to resize to available space
235223
useEffect(() => {
236224
if (isEditorReady) {

src/plugins/editor-monaco/utils/monaco-action-apidom-deref.js

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)