@theia/plugin-ext 1.76.0-next.10 → 1.76.0-next.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/main/browser/editors-and-documents-main.d.ts +7 -0
- package/lib/main/browser/editors-and-documents-main.d.ts.map +1 -1
- package/lib/main/browser/editors-and-documents-main.js +19 -9
- package/lib/main/browser/editors-and-documents-main.js.map +1 -1
- package/lib/main/browser/editors-and-documents-main.spec.d.ts +2 -0
- package/lib/main/browser/editors-and-documents-main.spec.d.ts.map +1 -0
- package/lib/main/browser/editors-and-documents-main.spec.js +117 -0
- package/lib/main/browser/editors-and-documents-main.spec.js.map +1 -0
- package/lib/main/browser/languages-main.d.ts +7 -0
- package/lib/main/browser/languages-main.d.ts.map +1 -1
- package/lib/main/browser/languages-main.js +31 -9
- package/lib/main/browser/languages-main.js.map +1 -1
- package/lib/main/browser/languages-main.spec.d.ts +2 -0
- package/lib/main/browser/languages-main.spec.d.ts.map +1 -0
- package/lib/main/browser/languages-main.spec.js +82 -0
- package/lib/main/browser/languages-main.spec.js.map +1 -0
- package/package.json +31 -31
- package/src/main/browser/editors-and-documents-main.spec.ts +140 -0
- package/src/main/browser/editors-and-documents-main.ts +19 -7
- package/src/main/browser/languages-main.spec.ts +101 -0
- package/src/main/browser/languages-main.ts +31 -9
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 JuliaHub, Inc. and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
|
|
18
|
+
// `languages-main` transitively pulls in Monaco and several frontend modules that touch `document`
|
|
19
|
+
// at module-load time.
|
|
20
|
+
const disableJSDOM = enableJSDOM();
|
|
21
|
+
import { expect } from 'chai';
|
|
22
|
+
import * as monaco from '@theia/monaco-editor-core';
|
|
23
|
+
import { mainWindow } from '@theia/monaco-editor-core/esm/vs/base/browser/window';
|
|
24
|
+
import { URI } from '@theia/core/lib/common/uri';
|
|
25
|
+
import { expectThrowsAsync } from '@theia/core/lib/common/test/expect';
|
|
26
|
+
import { CellEditType, CellUri } from '@theia/notebook/lib/common';
|
|
27
|
+
import { CellEditOperation } from '@theia/notebook/lib/browser/notebook-types';
|
|
28
|
+
import type { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model';
|
|
29
|
+
import { LanguagesMainImpl } from './languages-main';
|
|
30
|
+
|
|
31
|
+
after(() => disableJSDOM());
|
|
32
|
+
|
|
33
|
+
const notebookUri = new URI('file:///notebook.ipynb');
|
|
34
|
+
const cellHandle = 3;
|
|
35
|
+
const cellComponents = CellUri.generate(notebookUri, cellHandle).toComponents();
|
|
36
|
+
|
|
37
|
+
type FakeNotebookModel = Pick<NotebookModel, 'getCellIndexByHandle' | 'applyEdits'> & {
|
|
38
|
+
edits: CellEditOperation[][];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function createNotebookModel(handles: number[]): FakeNotebookModel {
|
|
42
|
+
const edits: CellEditOperation[][] = [];
|
|
43
|
+
return {
|
|
44
|
+
edits,
|
|
45
|
+
getCellIndexByHandle: handle => handles.indexOf(handle),
|
|
46
|
+
applyEdits: applied => { edits.push(applied); }
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Bypasses the constructor's RPC/container wiring.
|
|
52
|
+
*/
|
|
53
|
+
function createLanguagesMain(notebook?: FakeNotebookModel): LanguagesMainImpl {
|
|
54
|
+
const languagesMain = Object.create(LanguagesMainImpl.prototype) as LanguagesMainImpl;
|
|
55
|
+
(languagesMain as unknown as Record<string, unknown>).notebookService = {
|
|
56
|
+
getNotebookEditorModel: (uri: URI) => uri.toString() === notebookUri.toString() ? notebook : undefined
|
|
57
|
+
};
|
|
58
|
+
return languagesMain;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe('LanguagesMainImpl#$changeLanguage', () => {
|
|
62
|
+
|
|
63
|
+
before(() => {
|
|
64
|
+
// Resolving a language id boots Monaco's standalone services, whose theme service reaches for
|
|
65
|
+
// DOM APIs JSDOM does not provide. Monaco captures its window at module load.
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
|
+
(global as any).CSS ??= { escape: (value: string) => value };
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
|
+
(mainWindow as any).matchMedia ??= () => ({ matches: false, addEventListener: () => { }, removeEventListener: () => { } });
|
|
70
|
+
monaco.languages.register({ id: 'julia' });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('applies a cell language edit for a notebook cell', async () => {
|
|
74
|
+
const notebook = createNotebookModel([1, 2, cellHandle]);
|
|
75
|
+
const languagesMain = createLanguagesMain(notebook);
|
|
76
|
+
|
|
77
|
+
await languagesMain.$changeLanguage(cellComponents, 'julia');
|
|
78
|
+
|
|
79
|
+
expect(notebook.edits).to.deep.equal([[{ editType: CellEditType.CellLanguage, index: 2, language: 'julia' }]]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('rejects for a cell of a notebook that is not open', async () => {
|
|
83
|
+
const languagesMain = createLanguagesMain();
|
|
84
|
+
|
|
85
|
+
await expectThrowsAsync(languagesMain.$changeLanguage(cellComponents, 'julia'), 'Invalid uri');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('rejects for a cell handle the notebook does not know', async () => {
|
|
89
|
+
const languagesMain = createLanguagesMain(createNotebookModel([1, 2]));
|
|
90
|
+
|
|
91
|
+
await expectThrowsAsync(languagesMain.$changeLanguage(cellComponents, 'julia'), 'Invalid uri');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('rejects an unknown language id before touching the notebook', async () => {
|
|
95
|
+
const notebook = createNotebookModel([cellHandle]);
|
|
96
|
+
const languagesMain = createLanguagesMain(notebook);
|
|
97
|
+
|
|
98
|
+
await expectThrowsAsync(languagesMain.$changeLanguage(cellComponents, 'no-such-language'), /Unknown language ID/);
|
|
99
|
+
expect(notebook.edits).to.be.empty;
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -88,6 +88,8 @@ import { CodeActionTriggerKind } from '../../plugin/types-impl';
|
|
|
88
88
|
import { IReadonlyVSDataTransfer } from '@theia/monaco-editor-core/esm/vs/base/common/dataTransfer';
|
|
89
89
|
import { FileUploadService } from '@theia/filesystem/lib/common/upload/file-upload';
|
|
90
90
|
import { ILogger } from '@theia/core';
|
|
91
|
+
import { NotebookService } from '@theia/notebook/lib/browser';
|
|
92
|
+
import { CellEditType, CellUri } from '@theia/notebook/lib/common';
|
|
91
93
|
|
|
92
94
|
@injectable()
|
|
93
95
|
export class LanguagesMainImpl implements LanguagesMain, Disposable {
|
|
@@ -110,6 +112,9 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
|
|
|
110
112
|
@inject(FileUploadService)
|
|
111
113
|
protected readonly fileUploadService: FileUploadService;
|
|
112
114
|
|
|
115
|
+
@inject(NotebookService)
|
|
116
|
+
protected readonly notebookService: NotebookService;
|
|
117
|
+
|
|
113
118
|
@inject(ILogger) @named('plugin-ext:LanguagesMainImpl')
|
|
114
119
|
protected readonly logger: ILogger;
|
|
115
120
|
|
|
@@ -129,18 +134,35 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
|
|
|
129
134
|
return Promise.resolve(monaco.languages.getLanguages().map(l => l.id));
|
|
130
135
|
}
|
|
131
136
|
|
|
132
|
-
$changeLanguage(resource: UriComponents, languageId: string): Promise<void> {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!model) {
|
|
136
|
-
return Promise.reject(new Error('Invalid uri'));
|
|
137
|
+
async $changeLanguage(resource: UriComponents, languageId: string): Promise<void> {
|
|
138
|
+
if (!monaco.languages.getEncodedLanguageId(languageId)) {
|
|
139
|
+
throw new Error(`Unknown language ID: ${languageId}`);
|
|
137
140
|
}
|
|
138
|
-
const
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
+
const cell = CellUri.parse(URI.fromComponents(resource));
|
|
142
|
+
if (cell) {
|
|
143
|
+
this.changeCellLanguage(cell.notebook, cell.handle, languageId);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const model = monaco.editor.getModel(monaco.Uri.revive(resource));
|
|
147
|
+
if (!model) {
|
|
148
|
+
throw new Error('Invalid uri');
|
|
141
149
|
}
|
|
142
150
|
monaco.editor.setModelLanguage(model, languageId);
|
|
143
|
-
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The cell model owns a cell's language; kernel selection, serialization, and the cell editor all follow it.
|
|
155
|
+
*/
|
|
156
|
+
protected changeCellLanguage(notebookUri: URI, handle: number, languageId: string): void {
|
|
157
|
+
const notebook = this.notebookService.getNotebookEditorModel(notebookUri);
|
|
158
|
+
if (!notebook) {
|
|
159
|
+
throw new Error('Invalid uri');
|
|
160
|
+
}
|
|
161
|
+
const index = notebook.getCellIndexByHandle(handle);
|
|
162
|
+
if (index < 0) {
|
|
163
|
+
throw new Error('Invalid uri');
|
|
164
|
+
}
|
|
165
|
+
notebook.applyEdits([{ editType: CellEditType.CellLanguage, index, language: languageId }], true);
|
|
144
166
|
}
|
|
145
167
|
|
|
146
168
|
protected register(handle: number, service: Disposable): void {
|