@theia/monaco 1.77.0-next.49 → 1.77.0-next.56
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/browser/monaco-color-registry.js +1 -1
- package/lib/browser/monaco-color-registry.js.map +1 -1
- package/lib/browser/monaco-command.js +1 -1
- package/lib/browser/monaco-command.js.map +1 -1
- package/lib/browser/monaco-context-key-service.d.ts +1 -1
- package/lib/browser/monaco-context-key-service.d.ts.map +1 -1
- package/lib/browser/monaco-context-key-service.js.map +1 -1
- package/lib/browser/monaco-diff-editor.js +3 -2
- package/lib/browser/monaco-diff-editor.js.map +1 -1
- package/lib/browser/monaco-editor-model.js +2 -1
- package/lib/browser/monaco-editor-model.js.map +1 -1
- package/lib/browser/monaco-editor-overlay-button.js +2 -1
- package/lib/browser/monaco-editor-overlay-button.js.map +1 -1
- package/lib/browser/monaco-editor-provider.js +2 -2
- package/lib/browser/monaco-editor-provider.js.map +1 -1
- package/lib/browser/monaco-editor-service.js +1 -1
- package/lib/browser/monaco-editor-service.js.map +1 -1
- package/lib/browser/monaco-editor-zone-widget.js +2 -1
- package/lib/browser/monaco-editor-zone-widget.js.map +1 -1
- package/lib/browser/monaco-editor.d.ts +2 -0
- package/lib/browser/monaco-editor.d.ts.map +1 -1
- package/lib/browser/monaco-editor.js +12 -7
- package/lib/browser/monaco-editor.js.map +1 -1
- package/lib/browser/monaco-editor.spec.d.ts +2 -0
- package/lib/browser/monaco-editor.spec.d.ts.map +1 -0
- package/lib/browser/monaco-editor.spec.js +182 -0
- package/lib/browser/monaco-editor.spec.js.map +1 -0
- package/lib/browser/monaco-frontend-application-contribution.js +1 -1
- package/lib/browser/monaco-frontend-application-contribution.js.map +1 -1
- package/lib/browser/monaco-frontend-module.js +2 -1
- package/lib/browser/monaco-frontend-module.js.map +1 -1
- package/lib/browser/monaco-indexed-db.js +1 -1
- package/lib/browser/monaco-indexed-db.js.map +1 -1
- package/lib/browser/monaco-init.js +1 -1
- package/lib/browser/monaco-init.js.map +1 -1
- package/lib/browser/monaco-keybinding.js +1 -1
- package/lib/browser/monaco-keybinding.js.map +1 -1
- package/lib/browser/monaco-keycode-map.js +3 -2
- package/lib/browser/monaco-keycode-map.js.map +1 -1
- package/lib/browser/monaco-languages.js +2 -2
- package/lib/browser/monaco-languages.js.map +1 -1
- package/lib/browser/monaco-marker-collection.js +2 -1
- package/lib/browser/monaco-marker-collection.js.map +1 -1
- package/lib/browser/monaco-mime-service.js +1 -1
- package/lib/browser/monaco-mime-service.js.map +1 -1
- package/lib/browser/monaco-outline-contribution.d.ts +3 -0
- package/lib/browser/monaco-outline-contribution.d.ts.map +1 -1
- package/lib/browser/monaco-outline-contribution.js +6 -2
- package/lib/browser/monaco-outline-contribution.js.map +1 -1
- package/lib/browser/monaco-outline-decorator.d.ts.map +1 -1
- package/lib/browser/monaco-outline-decorator.js +8 -5
- package/lib/browser/monaco-outline-decorator.js.map +1 -1
- package/lib/browser/monaco-outline-decorator.spec.d.ts +2 -0
- package/lib/browser/monaco-outline-decorator.spec.d.ts.map +1 -0
- package/lib/browser/monaco-outline-decorator.spec.js +79 -0
- package/lib/browser/monaco-outline-decorator.spec.js.map +1 -0
- package/lib/browser/monaco-quick-input-service.d.ts +5 -0
- package/lib/browser/monaco-quick-input-service.d.ts.map +1 -1
- package/lib/browser/monaco-quick-input-service.js +8 -1
- package/lib/browser/monaco-quick-input-service.js.map +1 -1
- package/lib/browser/monaco-resolved-keybinding.js +2 -1
- package/lib/browser/monaco-resolved-keybinding.js.map +1 -1
- package/lib/browser/monaco-snippet-suggest-provider.js +3 -3
- package/lib/browser/monaco-snippet-suggest-provider.js.map +1 -1
- package/lib/browser/monaco-text-model-service.js +1 -1
- package/lib/browser/monaco-text-model-service.js.map +1 -1
- package/lib/browser/monaco-theming-service.js +3 -3
- package/lib/browser/monaco-theming-service.js.map +1 -1
- package/lib/browser/monaco-to-protocol-converter.js +1 -1
- package/lib/browser/monaco-to-protocol-converter.js.map +1 -1
- package/lib/browser/monaco-workspace.js +2 -2
- package/lib/browser/monaco-workspace.js.map +1 -1
- package/lib/browser/protocol-to-monaco-converter.js +1 -1
- package/lib/browser/protocol-to-monaco-converter.js.map +1 -1
- package/lib/browser/simple-monaco-editor.js +2 -1
- package/lib/browser/simple-monaco-editor.js.map +1 -1
- package/lib/browser/textmate/monaco-textmate-service.js +1 -1
- package/lib/browser/textmate/monaco-textmate-service.js.map +1 -1
- package/lib/browser/textmate/monaco-theme-registry.js +1 -1
- package/lib/browser/textmate/monaco-theme-registry.js.map +1 -1
- package/lib/browser/textmate/textmate-contribution.js +2 -1
- package/lib/browser/textmate/textmate-contribution.js.map +1 -1
- package/lib/browser/textmate/textmate-snippet-completion-provider.js +2 -1
- package/lib/browser/textmate/textmate-snippet-completion-provider.js.map +1 -1
- package/lib/browser/workspace-symbol-command.js +1 -1
- package/lib/browser/workspace-symbol-command.js.map +1 -1
- package/lib/package.spec.js +1 -0
- package/lib/package.spec.js.map +1 -1
- package/package.json +8 -8
- package/src/browser/monaco-context-key-service.ts +1 -1
- package/src/browser/monaco-diff-editor.ts +1 -1
- package/src/browser/monaco-editor-provider.ts +1 -1
- package/src/browser/monaco-editor.spec.ts +201 -0
- package/src/browser/monaco-editor.ts +11 -5
- package/src/browser/monaco-outline-contribution.ts +7 -0
- package/src/browser/monaco-outline-decorator.spec.ts +96 -0
- package/src/browser/monaco-outline-decorator.ts +8 -5
- package/src/browser/monaco-quick-input-service.ts +12 -0
|
@@ -126,7 +126,7 @@ export class MonacoDiffEditor extends MonacoEditor {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
override handleVisibilityChanged(nowVisible: boolean): void {
|
|
129
|
-
const isFirstShow = nowVisible && !this.savedViewState;
|
|
129
|
+
const isFirstShow = nowVisible && this.detachModelOnHide && !this.savedViewState;
|
|
130
130
|
super.handleVisibilityChanged(nowVisible);
|
|
131
131
|
if (isFirstShow) {
|
|
132
132
|
this._diffEditor.revealFirstDiff();
|
|
@@ -311,7 +311,7 @@ export class MonacoEditorProvider {
|
|
|
311
311
|
protected createOptions(prefixes: string[], uri: string, overrideIdentifier?: string): Record<string, any> {
|
|
312
312
|
const flat: Record<string, any> = {};
|
|
313
313
|
for (const preferenceName of Object.keys(this.editorPreferences)) {
|
|
314
|
-
flat[preferenceName] = (
|
|
314
|
+
flat[preferenceName] = (this.editorPreferences as any).get({ preferenceName, overrideIdentifier }, undefined, uri);
|
|
315
315
|
}
|
|
316
316
|
return Object.entries(flat).reduce((tree, [preferenceName, value]) => this.setOption(preferenceName, deepClone(value), prefixes, tree), {});
|
|
317
317
|
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource 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
|
+
let disableJSDOM = enableJSDOM();
|
|
19
|
+
|
|
20
|
+
import { expect } from 'chai';
|
|
21
|
+
import * as sinon from 'sinon';
|
|
22
|
+
import { Disposable, Event } from '@theia/core/lib/common';
|
|
23
|
+
import URI from '@theia/core/lib/common/uri';
|
|
24
|
+
import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
|
|
25
|
+
import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation';
|
|
26
|
+
import type { MonacoEditor as MonacoEditorType, MonacoEditorServices } from './monaco-editor';
|
|
27
|
+
import type { MonacoEditorModel } from './monaco-editor-model';
|
|
28
|
+
import type { MonacoDiffEditor as MonacoDiffEditorType } from './monaco-diff-editor';
|
|
29
|
+
|
|
30
|
+
disableJSDOM();
|
|
31
|
+
|
|
32
|
+
describe('MonacoEditor visibility', () => {
|
|
33
|
+
let MonacoEditor: typeof MonacoEditorType;
|
|
34
|
+
let MonacoDiffEditor: typeof MonacoDiffEditorType;
|
|
35
|
+
|
|
36
|
+
before(async () => {
|
|
37
|
+
disableJSDOM = enableJSDOM();
|
|
38
|
+
// Initialize baseline services before editor imports register additional services requiring browser CSS globals.
|
|
39
|
+
StandaloneServices.get(IInstantiationService);
|
|
40
|
+
({ MonacoEditor } = await import('./monaco-editor'));
|
|
41
|
+
({ MonacoDiffEditor } = await import('./monaco-diff-editor'));
|
|
42
|
+
});
|
|
43
|
+
after(() => disableJSDOM());
|
|
44
|
+
|
|
45
|
+
const model = {};
|
|
46
|
+
const viewState = {};
|
|
47
|
+
let editor: MonacoEditorType;
|
|
48
|
+
let control: ReturnType<typeof createControl>;
|
|
49
|
+
|
|
50
|
+
const createControl = (initiallyAttached = true) => {
|
|
51
|
+
let currentModel: object | undefined = initiallyAttached ? model : undefined;
|
|
52
|
+
return {
|
|
53
|
+
createContextKey: sinon.spy(),
|
|
54
|
+
setModel: sinon.spy((value: object | undefined) => currentModel = value),
|
|
55
|
+
getModel: () => currentModel,
|
|
56
|
+
saveViewState: sinon.stub().returns(viewState),
|
|
57
|
+
restoreViewState: sinon.spy(),
|
|
58
|
+
focus: sinon.spy(),
|
|
59
|
+
render: sinon.spy()
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
control = createControl();
|
|
65
|
+
editor = new class extends MonacoEditor {
|
|
66
|
+
constructor() {
|
|
67
|
+
super(new URI('file:///test.ts'), {
|
|
68
|
+
textEditorModel: model,
|
|
69
|
+
onDidChangeReadOnly: Event.None,
|
|
70
|
+
onDidChangeEncoding: Event.None
|
|
71
|
+
} as unknown as MonacoEditorModel, document.createElement('div'), {} as MonacoEditorServices);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
protected override create(): Disposable {
|
|
75
|
+
this.editor = control as unknown as typeof this.editor;
|
|
76
|
+
return Disposable.NULL;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
protected override addHandlers(): void { }
|
|
80
|
+
}();
|
|
81
|
+
});
|
|
82
|
+
afterEach(() => editor.dispose());
|
|
83
|
+
|
|
84
|
+
it('should detach on hide and restore the model and view state without focusing on show', () => {
|
|
85
|
+
expect(editor.detachModelOnHide).to.be.true;
|
|
86
|
+
editor.handleVisibilityChanged(false);
|
|
87
|
+
expect(control.saveViewState.calledOnce).to.be.true;
|
|
88
|
+
expect(control.setModel.calledOnce).to.be.true;
|
|
89
|
+
expect(control.getModel()).to.not.exist;
|
|
90
|
+
expect(control.focus.called).to.be.false;
|
|
91
|
+
|
|
92
|
+
editor.handleVisibilityChanged(true);
|
|
93
|
+
expect(control.setModel.secondCall.calledWithExactly(model)).to.be.true;
|
|
94
|
+
expect(control.restoreViewState.calledOnceWithExactly(viewState)).to.be.true;
|
|
95
|
+
expect(control.focus.called).to.be.false;
|
|
96
|
+
expect(control.setModel.secondCall.calledBefore(control.restoreViewState.firstCall)).to.be.true;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should retain the model and view state without focusing when opting out', () => {
|
|
100
|
+
editor.detachModelOnHide = false;
|
|
101
|
+
for (let index = 0; index < 2; index++) {
|
|
102
|
+
editor.handleVisibilityChanged(false);
|
|
103
|
+
expect(control.getModel()).to.equal(model);
|
|
104
|
+
editor.handleVisibilityChanged(true);
|
|
105
|
+
}
|
|
106
|
+
expect(control.setModel.called).to.be.false;
|
|
107
|
+
expect(control.saveViewState.called).to.be.false;
|
|
108
|
+
expect(control.restoreViewState.called).to.be.false;
|
|
109
|
+
expect(control.focus.called).to.be.false;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should stage and unstage a hidden editor for preview without focusing it', () => {
|
|
113
|
+
editor.handleVisibilityChanged(false);
|
|
114
|
+
editor.stageForPreview();
|
|
115
|
+
expect(control.getModel()).to.equal(model);
|
|
116
|
+
expect(control.restoreViewState.calledOnceWithExactly(viewState)).to.be.true;
|
|
117
|
+
expect(control.render.calledOnceWithExactly(true)).to.be.true;
|
|
118
|
+
editor.unstagePreview();
|
|
119
|
+
expect(control.getModel()).to.not.exist;
|
|
120
|
+
expect(control.saveViewState.calledTwice).to.be.true;
|
|
121
|
+
expect(control.focus.called).to.be.false;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should keep the model attached when showing an editor staged for preview', () => {
|
|
125
|
+
editor.handleVisibilityChanged(false);
|
|
126
|
+
editor.stageForPreview();
|
|
127
|
+
editor.handleVisibilityChanged(true);
|
|
128
|
+
editor.unstagePreview();
|
|
129
|
+
expect(control.getModel()).to.equal(model);
|
|
130
|
+
expect(control.setModel.callCount).to.equal(3);
|
|
131
|
+
expect(control.focus.called).to.be.false;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should not stage or unstage an attached model when opting out', () => {
|
|
135
|
+
editor.detachModelOnHide = false;
|
|
136
|
+
editor.handleVisibilityChanged(false);
|
|
137
|
+
editor.stageForPreview();
|
|
138
|
+
editor.unstagePreview();
|
|
139
|
+
expect(control.getModel()).to.equal(model);
|
|
140
|
+
expect(control.setModel.called).to.be.false;
|
|
141
|
+
expect(control.saveViewState.called).to.be.false;
|
|
142
|
+
expect(control.restoreViewState.called).to.be.false;
|
|
143
|
+
expect(control.render.called).to.be.false;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should reveal the first diff only on the initial show by default', () => {
|
|
147
|
+
const diffControl = { ...control, revealFirstDiff: sinon.spy() };
|
|
148
|
+
const diffEditor = Object.assign(Object.create(MonacoDiffEditor.prototype) as MonacoDiffEditorType, {
|
|
149
|
+
_diffEditor: diffControl,
|
|
150
|
+
diffEditorModel: model,
|
|
151
|
+
detachModelOnHide: true
|
|
152
|
+
});
|
|
153
|
+
diffEditor.handleVisibilityChanged(true);
|
|
154
|
+
expect(diffControl.revealFirstDiff.calledOnce).to.be.true;
|
|
155
|
+
diffEditor.handleVisibilityChanged(false);
|
|
156
|
+
diffEditor.handleVisibilityChanged(true);
|
|
157
|
+
expect(diffControl.revealFirstDiff.calledOnce).to.be.true;
|
|
158
|
+
expect(control.setModel.callCount).to.equal(3);
|
|
159
|
+
expect(control.restoreViewState.secondCall.calledWithExactly(viewState)).to.be.true;
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should initially attach a model-less diff editor without focusing when opting out', () => {
|
|
163
|
+
const diffControl = { ...createControl(false), revealFirstDiff: sinon.spy() };
|
|
164
|
+
const diffEditor = Object.assign(Object.create(MonacoDiffEditor.prototype) as MonacoDiffEditorType, {
|
|
165
|
+
_diffEditor: diffControl,
|
|
166
|
+
diffEditorModel: model,
|
|
167
|
+
detachModelOnHide: false
|
|
168
|
+
});
|
|
169
|
+
expect(diffControl.getModel()).to.be.undefined;
|
|
170
|
+
diffEditor.handleVisibilityChanged(true);
|
|
171
|
+
expect(diffControl.getModel()).to.equal(model);
|
|
172
|
+
expect(diffControl.setModel.calledOnceWithExactly(model)).to.be.true;
|
|
173
|
+
|
|
174
|
+
for (let index = 0; index < 2; index++) {
|
|
175
|
+
diffEditor.handleVisibilityChanged(false);
|
|
176
|
+
expect(diffControl.getModel()).to.equal(model);
|
|
177
|
+
diffEditor.handleVisibilityChanged(true);
|
|
178
|
+
}
|
|
179
|
+
expect(diffControl.setModel.calledOnceWithExactly(model)).to.be.true;
|
|
180
|
+
expect(diffControl.saveViewState.called).to.be.false;
|
|
181
|
+
expect(diffControl.restoreViewState.called).to.be.false;
|
|
182
|
+
expect(diffControl.revealFirstDiff.called).to.be.false;
|
|
183
|
+
expect(diffControl.focus.called).to.be.false;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should not reveal the first diff on each show when opting out', () => {
|
|
187
|
+
const diffControl = { ...control, revealFirstDiff: sinon.spy() };
|
|
188
|
+
const diffEditor = Object.assign(Object.create(MonacoDiffEditor.prototype) as MonacoDiffEditorType, {
|
|
189
|
+
_diffEditor: diffControl,
|
|
190
|
+
diffEditorModel: model,
|
|
191
|
+
detachModelOnHide: false
|
|
192
|
+
});
|
|
193
|
+
diffEditor.handleVisibilityChanged(false);
|
|
194
|
+
diffEditor.handleVisibilityChanged(true);
|
|
195
|
+
diffEditor.handleVisibilityChanged(false);
|
|
196
|
+
diffEditor.handleVisibilityChanged(true);
|
|
197
|
+
expect(diffControl.revealFirstDiff.called).to.be.false;
|
|
198
|
+
expect(control.setModel.called).to.be.false;
|
|
199
|
+
expect(control.restoreViewState.called).to.be.false;
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -106,6 +106,9 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor {
|
|
|
106
106
|
return instance;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/** Whether hiding the editor should detach its model and save its view state for restoration on show. */
|
|
110
|
+
detachModelOnHide = true;
|
|
111
|
+
|
|
109
112
|
protected readonly toDispose = new DisposableCollection();
|
|
110
113
|
|
|
111
114
|
protected readonly autoSizing: boolean;
|
|
@@ -265,12 +268,15 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor {
|
|
|
265
268
|
}
|
|
266
269
|
|
|
267
270
|
handleVisibilityChanged(nowVisible: boolean): void {
|
|
271
|
+
this._stagedForPreview = false;
|
|
268
272
|
if (nowVisible) {
|
|
269
|
-
this.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
if (this.detachModelOnHide || !this.baseEditor.getModel()) {
|
|
274
|
+
this.baseEditor.setModel(this.baseModel);
|
|
275
|
+
}
|
|
276
|
+
if (this.detachModelOnHide) {
|
|
277
|
+
this.baseEditor.restoreViewState(this.savedViewState);
|
|
278
|
+
}
|
|
279
|
+
} else if (this.detachModelOnHide) {
|
|
274
280
|
this.savedViewState = this.baseEditor.saveViewState();
|
|
275
281
|
|
|
276
282
|
// eslint-disable-next-line no-null/no-null
|
|
@@ -301,6 +301,7 @@ export class MonacoOutlineContribution implements FrontendApplicationContributio
|
|
|
301
301
|
iconClass: monaco.languages.SymbolKind[symbol.kind].toString().toLowerCase(),
|
|
302
302
|
name: this.getName(symbol),
|
|
303
303
|
detail: this.getDetail(symbol),
|
|
304
|
+
deprecated: this.isDeprecated(symbol),
|
|
304
305
|
parent,
|
|
305
306
|
uri,
|
|
306
307
|
range: this.getNameRange(symbol),
|
|
@@ -324,6 +325,10 @@ export class MonacoOutlineContribution implements FrontendApplicationContributio
|
|
|
324
325
|
return symbol.detail;
|
|
325
326
|
}
|
|
326
327
|
|
|
328
|
+
protected isDeprecated(symbol: monaco.languages.DocumentSymbol): boolean {
|
|
329
|
+
return !!symbol.tags?.includes(monaco.languages.SymbolTag.Deprecated);
|
|
330
|
+
}
|
|
331
|
+
|
|
327
332
|
protected createId(name: string, ids: Map<string, number>): string {
|
|
328
333
|
const counter = ids.get(name);
|
|
329
334
|
const index = typeof counter === 'number' ? counter + 1 : 0;
|
|
@@ -370,6 +375,8 @@ export interface MonacoOutlineSymbolInformationNode extends OutlineSymbolInforma
|
|
|
370
375
|
range: Range;
|
|
371
376
|
fullRange: Range;
|
|
372
377
|
detail?: string;
|
|
378
|
+
/** Whether the symbol is tagged as deprecated. */
|
|
379
|
+
deprecated?: boolean;
|
|
373
380
|
parent: MonacoOutlineSymbolInformationNode | undefined;
|
|
374
381
|
children: MonacoOutlineSymbolInformationNode[];
|
|
375
382
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 Vinay Kaushal 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
|
+
import { expect } from 'chai';
|
|
19
|
+
import URI from '@theia/core/lib/common/uri';
|
|
20
|
+
import type { CompositeTreeNode, Tree } from '@theia/core/lib/browser/tree';
|
|
21
|
+
import type { TreeDecoration } from '@theia/core/lib/browser/tree/tree-decorator';
|
|
22
|
+
import type * as monaco from '@theia/monaco-editor-core';
|
|
23
|
+
import type { MonacoOutlineSymbolInformationNode } from './monaco-outline-contribution';
|
|
24
|
+
|
|
25
|
+
describe('MonacoOutlineDecorator', () => {
|
|
26
|
+
|
|
27
|
+
// The outline code uses the full monaco API at runtime. Load it only once this suite runs: loading it while
|
|
28
|
+
// the spec files are collected registers monaco's editor contributions before monaco-init.spec initializes
|
|
29
|
+
// the standalone services, which then need browser APIs that JSDOM does not provide.
|
|
30
|
+
let monacoApi: typeof monaco;
|
|
31
|
+
let outline: typeof import('./monaco-outline-contribution');
|
|
32
|
+
let decorator: typeof import('./monaco-outline-decorator');
|
|
33
|
+
let disableJSDOM: () => void;
|
|
34
|
+
|
|
35
|
+
before(async () => {
|
|
36
|
+
disableJSDOM = enableJSDOM();
|
|
37
|
+
// @lumino/dragdrop (pulled in transitively) extends the DragEvent DOM global at
|
|
38
|
+
// module load, which JSDOM does not provide; stub it so the import succeeds.
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
if (!(global as any).DragEvent) {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
(global as any).DragEvent = class DragEvent extends (global as any).Event { };
|
|
43
|
+
}
|
|
44
|
+
monacoApi = await import('@theia/monaco-editor-core');
|
|
45
|
+
outline = await import('./monaco-outline-contribution');
|
|
46
|
+
decorator = await import('./monaco-outline-decorator');
|
|
47
|
+
});
|
|
48
|
+
after(() => disableJSDOM());
|
|
49
|
+
|
|
50
|
+
const range: monaco.IRange = { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 };
|
|
51
|
+
|
|
52
|
+
function symbol(name: string, detail: string, deprecated = false): monaco.languages.DocumentSymbol {
|
|
53
|
+
const tags = deprecated ? [monacoApi.languages.SymbolTag.Deprecated] : [];
|
|
54
|
+
return { name, detail, kind: monacoApi.languages.SymbolKind.Method, tags, range, selectionRange: range };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function createNode(documentSymbol: monaco.languages.DocumentSymbol): MonacoOutlineSymbolInformationNode {
|
|
58
|
+
return new outline.MonacoOutlineContribution()['createNode'](new URI('file:///test.ts'), documentSymbol, new Map());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function decorate(...nodes: MonacoOutlineSymbolInformationNode[]): Promise<Map<string, TreeDecoration.Data>> {
|
|
62
|
+
const root: CompositeTreeNode = { id: 'root', name: 'root', parent: undefined, children: nodes };
|
|
63
|
+
return new decorator.MonacoOutlineDecorator().decorations({ root } as unknown as Tree);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
it('marks a symbol tagged as deprecated', () => {
|
|
67
|
+
expect(createNode(symbol('old', '', true)).deprecated).to.equal(true);
|
|
68
|
+
expect(createNode(symbol('current', '')).deprecated).to.equal(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('strikes through a deprecated symbol', async () => {
|
|
72
|
+
const node = createNode(symbol('old', '', true));
|
|
73
|
+
|
|
74
|
+
const decorations = await decorate(node);
|
|
75
|
+
|
|
76
|
+
expect(decorations.get(node.id)?.fontData).to.deep.equal({ style: 'line-through' });
|
|
77
|
+
expect(decorations.get(node.id)?.captionSuffixes).to.equal(undefined);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('keeps the detail suffix of a deprecated symbol', async () => {
|
|
81
|
+
const node = createNode(symbol('old', '(): void', true));
|
|
82
|
+
|
|
83
|
+
const decorations = await decorate(node);
|
|
84
|
+
|
|
85
|
+
expect(decorations.get(node.id)?.fontData).to.deep.equal({ style: 'line-through' });
|
|
86
|
+
expect(decorations.get(node.id)?.captionSuffixes?.map(suffix => suffix.data)).to.deep.equal(['(): void']);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not decorate a symbol that is neither deprecated nor has a detail', async () => {
|
|
90
|
+
const node = createNode(symbol('current', ''));
|
|
91
|
+
|
|
92
|
+
const decorations = await decorate(node);
|
|
93
|
+
|
|
94
|
+
expect(decorations.has(node.id)).to.equal(false);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -43,7 +43,7 @@ export class MonacoOutlineDecorator implements TreeDecorator {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
for (const treeNode of new DepthFirstTreeIterator(tree.root)) {
|
|
46
|
-
if (MonacoOutlineSymbolInformationNode.is(treeNode) && treeNode.detail) {
|
|
46
|
+
if (MonacoOutlineSymbolInformationNode.is(treeNode) && (treeNode.detail || treeNode.deprecated)) {
|
|
47
47
|
result.set(treeNode.id, this.toDecoration(treeNode));
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -52,15 +52,18 @@ export class MonacoOutlineDecorator implements TreeDecorator {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
protected toDecoration(node: MonacoOutlineSymbolInformationNode): TreeDecoration.Data {
|
|
55
|
-
const captionSuffixes: TreeDecoration.CaptionAffix[] = [{
|
|
56
|
-
data:
|
|
55
|
+
const captionSuffixes: TreeDecoration.CaptionAffix[] | undefined = node.detail ? [{
|
|
56
|
+
data: node.detail,
|
|
57
57
|
fontData: {
|
|
58
58
|
color: 'var(--theia-descriptionForeground)',
|
|
59
59
|
}
|
|
60
|
-
}];
|
|
60
|
+
}] : undefined;
|
|
61
|
+
// Like VS Code, strike through the names of deprecated symbols.
|
|
62
|
+
const fontData: TreeDecoration.FontData | undefined = node.deprecated ? { style: 'line-through' } : undefined;
|
|
61
63
|
|
|
62
64
|
return {
|
|
63
|
-
captionSuffixes
|
|
65
|
+
captionSuffixes,
|
|
66
|
+
fontData
|
|
64
67
|
};
|
|
65
68
|
}
|
|
66
69
|
}
|
|
@@ -114,6 +114,11 @@ export class MonacoQuickInputImplementation implements IQuickInputService {
|
|
|
114
114
|
|
|
115
115
|
protected container: HTMLElement;
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* The element that had focus when the quick input was shown. Cleared when the quick input is hidden.
|
|
119
|
+
*/
|
|
120
|
+
previousFocusElement: HTMLElement | undefined;
|
|
121
|
+
|
|
117
122
|
protected inQuickOpen: IContextKey<boolean>;
|
|
118
123
|
|
|
119
124
|
/**
|
|
@@ -144,11 +149,17 @@ export class MonacoQuickInputImplementation implements IQuickInputService {
|
|
|
144
149
|
this.scopedInQuickOpen = this.scopedContextKeyService.createKey<boolean>('inQuickOpen', false);
|
|
145
150
|
|
|
146
151
|
this.controller.onShow(() => {
|
|
152
|
+
// `onShow` fires before focus moves into the quick input, and again whenever it switches to another picker.
|
|
153
|
+
const activeElement = document.activeElement;
|
|
154
|
+
if (activeElement instanceof HTMLElement && !this.container.contains(activeElement)) {
|
|
155
|
+
this.previousFocusElement = activeElement;
|
|
156
|
+
}
|
|
147
157
|
this.container.style.top = this.shell.mainPanel.node.getBoundingClientRect().top + 'px';
|
|
148
158
|
this.inQuickOpen.set(true);
|
|
149
159
|
this.scopedInQuickOpen.set(true);
|
|
150
160
|
});
|
|
151
161
|
this.controller.onHide(() => {
|
|
162
|
+
this.previousFocusElement = undefined;
|
|
152
163
|
this.inQuickOpen.set(false);
|
|
153
164
|
this.scopedInQuickOpen.set(false);
|
|
154
165
|
});
|
|
@@ -407,6 +418,7 @@ export class MonacoQuickInputService implements QuickInputService {
|
|
|
407
418
|
|
|
408
419
|
get onShow(): Event<void> { return this.monacoService.onShow; }
|
|
409
420
|
get onHide(): Event<void> { return this.monacoService.onHide; }
|
|
421
|
+
get previousFocusElement(): HTMLElement | undefined { return this.monacoService.previousFocusElement; }
|
|
410
422
|
|
|
411
423
|
open(filter: string): void {
|
|
412
424
|
this.monacoService.open(filter);
|