@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.
Files changed (98) hide show
  1. package/lib/browser/monaco-color-registry.js +1 -1
  2. package/lib/browser/monaco-color-registry.js.map +1 -1
  3. package/lib/browser/monaco-command.js +1 -1
  4. package/lib/browser/monaco-command.js.map +1 -1
  5. package/lib/browser/monaco-context-key-service.d.ts +1 -1
  6. package/lib/browser/monaco-context-key-service.d.ts.map +1 -1
  7. package/lib/browser/monaco-context-key-service.js.map +1 -1
  8. package/lib/browser/monaco-diff-editor.js +3 -2
  9. package/lib/browser/monaco-diff-editor.js.map +1 -1
  10. package/lib/browser/monaco-editor-model.js +2 -1
  11. package/lib/browser/monaco-editor-model.js.map +1 -1
  12. package/lib/browser/monaco-editor-overlay-button.js +2 -1
  13. package/lib/browser/monaco-editor-overlay-button.js.map +1 -1
  14. package/lib/browser/monaco-editor-provider.js +2 -2
  15. package/lib/browser/monaco-editor-provider.js.map +1 -1
  16. package/lib/browser/monaco-editor-service.js +1 -1
  17. package/lib/browser/monaco-editor-service.js.map +1 -1
  18. package/lib/browser/monaco-editor-zone-widget.js +2 -1
  19. package/lib/browser/monaco-editor-zone-widget.js.map +1 -1
  20. package/lib/browser/monaco-editor.d.ts +2 -0
  21. package/lib/browser/monaco-editor.d.ts.map +1 -1
  22. package/lib/browser/monaco-editor.js +12 -7
  23. package/lib/browser/monaco-editor.js.map +1 -1
  24. package/lib/browser/monaco-editor.spec.d.ts +2 -0
  25. package/lib/browser/monaco-editor.spec.d.ts.map +1 -0
  26. package/lib/browser/monaco-editor.spec.js +182 -0
  27. package/lib/browser/monaco-editor.spec.js.map +1 -0
  28. package/lib/browser/monaco-frontend-application-contribution.js +1 -1
  29. package/lib/browser/monaco-frontend-application-contribution.js.map +1 -1
  30. package/lib/browser/monaco-frontend-module.js +2 -1
  31. package/lib/browser/monaco-frontend-module.js.map +1 -1
  32. package/lib/browser/monaco-indexed-db.js +1 -1
  33. package/lib/browser/monaco-indexed-db.js.map +1 -1
  34. package/lib/browser/monaco-init.js +1 -1
  35. package/lib/browser/monaco-init.js.map +1 -1
  36. package/lib/browser/monaco-keybinding.js +1 -1
  37. package/lib/browser/monaco-keybinding.js.map +1 -1
  38. package/lib/browser/monaco-keycode-map.js +3 -2
  39. package/lib/browser/monaco-keycode-map.js.map +1 -1
  40. package/lib/browser/monaco-languages.js +2 -2
  41. package/lib/browser/monaco-languages.js.map +1 -1
  42. package/lib/browser/monaco-marker-collection.js +2 -1
  43. package/lib/browser/monaco-marker-collection.js.map +1 -1
  44. package/lib/browser/monaco-mime-service.js +1 -1
  45. package/lib/browser/monaco-mime-service.js.map +1 -1
  46. package/lib/browser/monaco-outline-contribution.d.ts +3 -0
  47. package/lib/browser/monaco-outline-contribution.d.ts.map +1 -1
  48. package/lib/browser/monaco-outline-contribution.js +6 -2
  49. package/lib/browser/monaco-outline-contribution.js.map +1 -1
  50. package/lib/browser/monaco-outline-decorator.d.ts.map +1 -1
  51. package/lib/browser/monaco-outline-decorator.js +8 -5
  52. package/lib/browser/monaco-outline-decorator.js.map +1 -1
  53. package/lib/browser/monaco-outline-decorator.spec.d.ts +2 -0
  54. package/lib/browser/monaco-outline-decorator.spec.d.ts.map +1 -0
  55. package/lib/browser/monaco-outline-decorator.spec.js +79 -0
  56. package/lib/browser/monaco-outline-decorator.spec.js.map +1 -0
  57. package/lib/browser/monaco-quick-input-service.d.ts +5 -0
  58. package/lib/browser/monaco-quick-input-service.d.ts.map +1 -1
  59. package/lib/browser/monaco-quick-input-service.js +8 -1
  60. package/lib/browser/monaco-quick-input-service.js.map +1 -1
  61. package/lib/browser/monaco-resolved-keybinding.js +2 -1
  62. package/lib/browser/monaco-resolved-keybinding.js.map +1 -1
  63. package/lib/browser/monaco-snippet-suggest-provider.js +3 -3
  64. package/lib/browser/monaco-snippet-suggest-provider.js.map +1 -1
  65. package/lib/browser/monaco-text-model-service.js +1 -1
  66. package/lib/browser/monaco-text-model-service.js.map +1 -1
  67. package/lib/browser/monaco-theming-service.js +3 -3
  68. package/lib/browser/monaco-theming-service.js.map +1 -1
  69. package/lib/browser/monaco-to-protocol-converter.js +1 -1
  70. package/lib/browser/monaco-to-protocol-converter.js.map +1 -1
  71. package/lib/browser/monaco-workspace.js +2 -2
  72. package/lib/browser/monaco-workspace.js.map +1 -1
  73. package/lib/browser/protocol-to-monaco-converter.js +1 -1
  74. package/lib/browser/protocol-to-monaco-converter.js.map +1 -1
  75. package/lib/browser/simple-monaco-editor.js +2 -1
  76. package/lib/browser/simple-monaco-editor.js.map +1 -1
  77. package/lib/browser/textmate/monaco-textmate-service.js +1 -1
  78. package/lib/browser/textmate/monaco-textmate-service.js.map +1 -1
  79. package/lib/browser/textmate/monaco-theme-registry.js +1 -1
  80. package/lib/browser/textmate/monaco-theme-registry.js.map +1 -1
  81. package/lib/browser/textmate/textmate-contribution.js +2 -1
  82. package/lib/browser/textmate/textmate-contribution.js.map +1 -1
  83. package/lib/browser/textmate/textmate-snippet-completion-provider.js +2 -1
  84. package/lib/browser/textmate/textmate-snippet-completion-provider.js.map +1 -1
  85. package/lib/browser/workspace-symbol-command.js +1 -1
  86. package/lib/browser/workspace-symbol-command.js.map +1 -1
  87. package/lib/package.spec.js +1 -0
  88. package/lib/package.spec.js.map +1 -1
  89. package/package.json +8 -8
  90. package/src/browser/monaco-context-key-service.ts +1 -1
  91. package/src/browser/monaco-diff-editor.ts +1 -1
  92. package/src/browser/monaco-editor-provider.ts +1 -1
  93. package/src/browser/monaco-editor.spec.ts +201 -0
  94. package/src/browser/monaco-editor.ts +11 -5
  95. package/src/browser/monaco-outline-contribution.ts +7 -0
  96. package/src/browser/monaco-outline-decorator.spec.ts +96 -0
  97. package/src/browser/monaco-outline-decorator.ts +8 -5
  98. 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] = (<any>this.editorPreferences).get({ preferenceName, overrideIdentifier }, undefined, uri);
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._stagedForPreview = false;
270
- this.baseEditor.setModel(this.baseModel);
271
- this.baseEditor.restoreViewState(this.savedViewState);
272
- } else {
273
- this._stagedForPreview = false;
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: (node.detail || ''),
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);