@theia/monaco 1.61.0 → 1.62.0

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 (34) hide show
  1. package/lib/browser/monaco-code-action-save-participant.d.ts +14 -0
  2. package/lib/browser/monaco-code-action-save-participant.d.ts.map +1 -0
  3. package/lib/browser/monaco-code-action-save-participant.js +129 -0
  4. package/lib/browser/monaco-code-action-save-participant.js.map +1 -0
  5. package/lib/browser/monaco-editor-model.d.ts +8 -13
  6. package/lib/browser/monaco-editor-model.d.ts.map +1 -1
  7. package/lib/browser/monaco-editor-model.js +21 -57
  8. package/lib/browser/monaco-editor-model.js.map +1 -1
  9. package/lib/browser/monaco-editor-provider.d.ts +18 -7
  10. package/lib/browser/monaco-editor-provider.d.ts.map +1 -1
  11. package/lib/browser/monaco-editor-provider.js +98 -51
  12. package/lib/browser/monaco-editor-provider.js.map +1 -1
  13. package/lib/browser/monaco-editor-service.d.ts +10 -2
  14. package/lib/browser/monaco-editor-service.d.ts.map +1 -1
  15. package/lib/browser/monaco-editor-service.js +21 -2
  16. package/lib/browser/monaco-editor-service.js.map +1 -1
  17. package/lib/browser/monaco-frontend-module.d.ts.map +1 -1
  18. package/lib/browser/monaco-frontend-module.js +5 -0
  19. package/lib/browser/monaco-frontend-module.js.map +1 -1
  20. package/lib/browser/monaco-menu.d.ts.map +1 -1
  21. package/lib/browser/monaco-menu.js +5 -4
  22. package/lib/browser/monaco-menu.js.map +1 -1
  23. package/lib/browser/monaco-workspace.d.ts +1 -4
  24. package/lib/browser/monaco-workspace.d.ts.map +1 -1
  25. package/lib/browser/monaco-workspace.js +0 -6
  26. package/lib/browser/monaco-workspace.js.map +1 -1
  27. package/package.json +9 -9
  28. package/src/browser/monaco-code-action-save-participant.ts +143 -0
  29. package/src/browser/monaco-editor-model.ts +25 -74
  30. package/src/browser/monaco-editor-provider.ts +115 -55
  31. package/src/browser/monaco-editor-service.ts +24 -3
  32. package/src/browser/monaco-frontend-module.ts +8 -2
  33. package/src/browser/monaco-menu.ts +5 -4
  34. package/src/browser/monaco-workspace.ts +1 -9
@@ -18,26 +18,23 @@
18
18
  import URI from '@theia/core/lib/common/uri';
19
19
  import { EditorPreferenceChange, EditorPreferences, TextEditor, DiffNavigator } from '@theia/editor/lib/browser';
20
20
  import { DiffUris } from '@theia/core/lib/browser/diff-uris';
21
- import { inject, injectable, named } from '@theia/core/shared/inversify';
22
- import { DisposableCollection, deepClone, Disposable } from '@theia/core/lib/common';
23
- import { TextDocumentSaveReason } from '@theia/core/shared/vscode-languageserver-protocol';
21
+ import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
22
+ import { DisposableCollection, deepClone, Disposable, CancellationToken } from '@theia/core/lib/common';
24
23
  import { MonacoDiffEditor } from './monaco-diff-editor';
25
24
  import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory';
26
25
  import { EditorServiceOverrides, MonacoEditor, MonacoEditorServices } from './monaco-editor';
27
- import { MonacoEditorModel, WillSaveMonacoModelEvent } from './monaco-editor-model';
26
+ import { MonacoEditorModel, TextDocumentSaveReason } from './monaco-editor-model';
28
27
  import { MonacoWorkspace } from './monaco-workspace';
29
28
  import { ContributionProvider } from '@theia/core';
30
- import { KeybindingRegistry, OpenerService, open, WidgetOpenerOptions, FormatType } from '@theia/core/lib/browser';
29
+ import { KeybindingRegistry, OpenerService, open, WidgetOpenerOptions, SaveOptions, FormatType } from '@theia/core/lib/browser';
31
30
  import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding';
32
31
  import { HttpOpenHandlerOptions } from '@theia/core/lib/browser/http-open-handler';
33
32
  import { MonacoToProtocolConverter } from './monaco-to-protocol-converter';
34
33
  import { ProtocolToMonacoConverter } from './protocol-to-monaco-converter';
35
- import { FileSystemPreferences } from '@theia/filesystem/lib/browser';
36
34
  import * as monaco from '@theia/monaco-editor-core';
37
35
  import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
38
36
  import { IOpenerService, OpenExternalOptions, OpenInternalOptions } from '@theia/monaco-editor-core/esm/vs/platform/opener/common/opener';
39
37
  import { IKeybindingService } from '@theia/monaco-editor-core/esm/vs/platform/keybinding/common/keybinding';
40
- import { timeoutReject } from '@theia/core/lib/common/promise-util';
41
38
  import { IContextMenuService } from '@theia/monaco-editor-core/esm/vs/platform/contextview/browser/contextView';
42
39
  import { KeyCodeChord } from '@theia/monaco-editor-core/esm/vs/base/common/keybindings';
43
40
  import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey';
@@ -46,6 +43,8 @@ import { IReference } from '@theia/monaco-editor-core/esm/vs/base/common/lifecyc
46
43
  import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
47
44
  import { SimpleMonacoEditor } from './simple-monaco-editor';
48
45
  import { ICodeEditorWidgetOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/widget/codeEditor/codeEditorWidget';
46
+ import { timeoutReject } from '@theia/core/lib/common/promise-util';
47
+ import { FileSystemPreferences } from '@theia/filesystem/lib/browser';
49
48
 
50
49
  export const MonacoEditorFactory = Symbol('MonacoEditorFactory');
51
50
  export interface MonacoEditorFactory {
@@ -53,6 +52,17 @@ export interface MonacoEditorFactory {
53
52
  create(model: MonacoEditorModel, defaultOptions: MonacoEditor.IOptions, defaultOverrides: EditorServiceOverrides): Promise<MonacoEditor>;
54
53
  }
55
54
 
55
+ export const SaveParticipant = Symbol('SaveParticipant');
56
+
57
+ export interface SaveParticipant {
58
+ readonly order: number;
59
+ applyChangesOnSave(
60
+ editor: MonacoEditor,
61
+ cancellationToken: CancellationToken,
62
+ options?: SaveOptions): Promise<void>;
63
+ }
64
+ export const SAVE_PARTICIPANT_DEFAULT_ORDER = 0;
65
+
56
66
  @injectable()
57
67
  export class MonacoEditorProvider {
58
68
 
@@ -68,9 +78,13 @@ export class MonacoEditorProvider {
68
78
 
69
79
  @inject(OpenerService)
70
80
  protected readonly openerService: OpenerService;
81
+ @inject(ContributionProvider)
82
+ @named(SaveParticipant)
83
+ protected readonly saveProviderContributions: ContributionProvider<SaveParticipant>;
71
84
 
72
85
  @inject(FileSystemPreferences)
73
86
  protected readonly filePreferences: FileSystemPreferences;
87
+ protected saveParticipants: SaveParticipant[];
74
88
 
75
89
  protected _current: MonacoEditor | undefined;
76
90
  /**
@@ -210,7 +224,7 @@ export class MonacoEditorProvider {
210
224
  }));
211
225
  toDispose.push(editor.onLanguageChanged(() => this.updateMonacoEditorOptions(editor)));
212
226
  toDispose.push(editor.onDidChangeReadOnly(() => this.updateReadOnlyMessage(options, model.readOnly)));
213
- editor.document.onWillSaveModel(event => event.waitUntil(this.formatOnSave(editor, event)));
227
+ toDispose.push(editor.document.registerWillSaveModelListener((_, token, o) => this.runSaveParticipants(editor, token, o)));
214
228
  return editor;
215
229
  }
216
230
 
@@ -239,46 +253,6 @@ export class MonacoEditorProvider {
239
253
  }
240
254
  }
241
255
 
242
- protected shouldFormat(editor: MonacoEditor, event: WillSaveMonacoModelEvent): boolean {
243
- if (event.reason !== TextDocumentSaveReason.Manual) {
244
- return false;
245
- }
246
- if (event.options?.formatType) {
247
- switch (event.options.formatType) {
248
- case FormatType.ON: return true;
249
- case FormatType.OFF: return false;
250
- case FormatType.DIRTY: return editor.document.dirty;
251
- }
252
- }
253
- return true;
254
- }
255
-
256
- protected async formatOnSave(editor: MonacoEditor, event: WillSaveMonacoModelEvent): Promise<monaco.editor.IIdentifiedSingleEditOperation[]> {
257
- if (!this.shouldFormat(editor, event)) {
258
- return [];
259
- }
260
- const edits: monaco.editor.IIdentifiedSingleEditOperation[] = [];
261
- const overrideIdentifier = editor.document.languageId;
262
- const uri = editor.uri.toString();
263
- const formatOnSave = this.editorPreferences.get({ preferenceName: 'editor.formatOnSave', overrideIdentifier }, undefined, uri);
264
- if (formatOnSave) {
265
- const formatOnSaveTimeout = this.editorPreferences.get({ preferenceName: 'editor.formatOnSaveTimeout', overrideIdentifier }, undefined, uri)!;
266
- await Promise.race([
267
- timeoutReject(formatOnSaveTimeout, `Aborted format on save after ${formatOnSaveTimeout}ms`),
268
- editor.runAction('editor.action.formatDocument')
269
- ]);
270
- }
271
- const shouldRemoveWhiteSpace = this.filePreferences.get({ preferenceName: 'files.trimTrailingWhitespace', overrideIdentifier }, undefined, uri);
272
- if (shouldRemoveWhiteSpace) {
273
- await editor.runAction('editor.action.trimTrailingWhitespace');
274
- }
275
- const insertFinalNewline = this.filePreferences.get({ preferenceName: 'files.insertFinalNewline', overrideIdentifier }, undefined, uri);
276
- if (insertFinalNewline) {
277
- edits.push(...this.insertFinalNewline(editor));
278
- }
279
- return edits;
280
- }
281
-
282
256
  protected get diffPreferencePrefixes(): string[] {
283
257
  return [...this.preferencePrefixes, 'diffEditor.'];
284
258
  }
@@ -497,20 +471,106 @@ export class MonacoEditorProvider {
497
471
  );
498
472
  }
499
473
 
500
- protected insertFinalNewline(editor: MonacoEditor): monaco.editor.IIdentifiedSingleEditOperation[] {
501
- const model = editor.document && editor.document.textEditorModel;
474
+ @postConstruct()
475
+ init(): void {
476
+ this.saveParticipants = this.saveProviderContributions.getContributions().slice().sort((left, right) => left.order - right.order);
477
+ this.registerSaveParticipant({
478
+ order: 1000,
479
+ applyChangesOnSave: (
480
+ editor: MonacoEditor,
481
+ cancellationToken: monaco.CancellationToken,
482
+ options: SaveOptions): Promise<void> => this.formatOnSave(editor, editor.document, cancellationToken, options)
483
+ });
484
+ }
485
+
486
+ registerSaveParticipant(saveParticipant: SaveParticipant): Disposable {
487
+ if (this.saveParticipants.find(value => value === saveParticipant)) {
488
+ throw new Error('Save participant already registered');
489
+ }
490
+ this.saveParticipants.push(saveParticipant);
491
+ this.saveParticipants.sort((left, right) => left.order - right.order);
492
+ return Disposable.create(() => {
493
+ const index = this.saveParticipants.indexOf(saveParticipant);
494
+ if (index >= 0) {
495
+ this.saveParticipants.splice(index, 1);
496
+ }
497
+ });
498
+ }
499
+
500
+ protected shouldFormat(model: MonacoEditorModel, options: SaveOptions): boolean {
501
+ if (options.saveReason !== TextDocumentSaveReason.Manual) {
502
+ return false;
503
+ }
504
+ switch (options.formatType) {
505
+ case FormatType.ON: return true;
506
+ case FormatType.OFF: return false;
507
+ case FormatType.DIRTY: return model.dirty;
508
+ }
509
+ return true;
510
+ }
511
+
512
+ async runSaveParticipants(editor: MonacoEditor, cancellationToken: CancellationToken, options?: SaveOptions): Promise<void> {
513
+ const initialState = editor.document.createSnapshot();
514
+ for (const participant of this.saveParticipants) {
515
+ if (cancellationToken.isCancellationRequested) {
516
+ break;
517
+ }
518
+ const snapshot = editor.document.createSnapshot();
519
+ try {
520
+ await participant.applyChangesOnSave(editor, cancellationToken, options);
521
+ } catch (e) {
522
+ console.error(e);
523
+ editor.document.applySnapshot(snapshot);
524
+ }
525
+ }
526
+ if (cancellationToken.isCancellationRequested) {
527
+ editor.document.applySnapshot(initialState);
528
+ }
529
+ }
530
+
531
+ protected async formatOnSave(
532
+ editor: MonacoEditor,
533
+ model: MonacoEditorModel,
534
+ cancellationToken: CancellationToken,
535
+ options: SaveOptions): Promise<void> {
536
+ if (!this.shouldFormat(model, options)) {
537
+ return;
538
+ }
539
+
540
+ const overrideIdentifier = model.languageId;
541
+ const uri = model.uri.toString();
542
+ const formatOnSave = this.editorPreferences.get({ preferenceName: 'editor.formatOnSave', overrideIdentifier }, undefined, uri);
543
+ if (formatOnSave) {
544
+ const formatOnSaveTimeout = this.editorPreferences.get({ preferenceName: 'editor.formatOnSaveTimeout', overrideIdentifier }, undefined, uri)!;
545
+ await Promise.race([
546
+ timeoutReject(formatOnSaveTimeout, `Aborted format on save after ${formatOnSaveTimeout}ms`),
547
+ await editor.runAction('editor.action.formatDocument')
548
+ ]);
549
+ }
550
+ const shouldRemoveWhiteSpace = this.filePreferences.get({ preferenceName: 'files.trimTrailingWhitespace', overrideIdentifier }, undefined, uri);
551
+ if (shouldRemoveWhiteSpace) {
552
+ await editor.runAction('editor.action.trimTrailingWhitespace');
553
+ }
554
+ const insertFinalNewline = this.filePreferences.get({ preferenceName: 'files.insertFinalNewline', overrideIdentifier }, undefined, uri);
555
+ if (insertFinalNewline) {
556
+ this.insertFinalNewline(model);
557
+ }
558
+ }
559
+
560
+ protected insertFinalNewline(editorModel: MonacoEditorModel): void {
561
+ const model = editorModel.textEditorModel;
502
562
  if (!model) {
503
- return [];
563
+ return;
504
564
  }
505
565
 
506
566
  const lines = model?.getLineCount();
507
567
  if (lines === 0) {
508
- return [];
568
+ return;
509
569
  }
510
570
 
511
571
  const lastLine = model?.getLineContent(lines);
512
572
  if (lastLine.trim() === '') {
513
- return [];
573
+ return;
514
574
  }
515
575
 
516
576
  const lastLineMaxColumn = model?.getLineMaxColumn(lines);
@@ -520,9 +580,9 @@ export class MonacoEditorProvider {
520
580
  endLineNumber: lines,
521
581
  endColumn: lastLineMaxColumn
522
582
  };
523
- return [{
583
+ model.applyEdits([{
524
584
  range,
525
585
  text: model?.getEOL()
526
- }];
586
+ }]);
527
587
  }
528
588
  }
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { injectable, inject, decorate } from '@theia/core/shared/inversify';
17
+ import { injectable, inject, decorate, named } from '@theia/core/shared/inversify';
18
18
  import URI from '@theia/core/lib/common/uri';
19
19
  import { OpenerService, open, WidgetOpenMode, ApplicationShell, PreferenceService } from '@theia/core/lib/browser';
20
20
  import { EditorWidget, EditorOpenerOptions, EditorManager, CustomEditorWidget } from '@theia/editor/lib/browser';
@@ -27,6 +27,7 @@ import { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/st
27
27
  import { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
28
28
  import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey';
29
29
  import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService';
30
+ import { ContributionProvider } from '@theia/core';
30
31
 
31
32
  decorate(injectable(), StandaloneCodeEditorService);
32
33
 
@@ -36,6 +37,14 @@ export const VSCodeThemeService = Symbol('VSCodeThemeService');
36
37
  export const MonacoEditorServiceFactory = Symbol('MonacoEditorServiceFactory');
37
38
  export type MonacoEditorServiceFactoryType = (contextKeyService: IContextKeyService, themeService: IThemeService) => MonacoEditorService;
38
39
 
40
+ /**
41
+ * contribution provider to extend the active editor handling to other editor types than just standalone editor widgets.
42
+ */
43
+ export const ActiveMonacoEditorContribution = Symbol('ActiveMonacoEditorContribution');
44
+ export interface ActiveMonacoEditorContribution {
45
+ getActiveEditor(): ICodeEditor | undefined;
46
+ }
47
+
39
48
  @injectable()
40
49
  export class MonacoEditorService extends StandaloneCodeEditorService {
41
50
 
@@ -56,6 +65,9 @@ export class MonacoEditorService extends StandaloneCodeEditorService {
56
65
  @inject(PreferenceService)
57
66
  protected readonly preferencesService: PreferenceService;
58
67
 
68
+ @inject(ContributionProvider) @named(ActiveMonacoEditorContribution)
69
+ protected readonly activeMonacoEditorContribution: ContributionProvider<ActiveMonacoEditorContribution>;
70
+
59
71
  constructor(@inject(VSCodeContextKeyService) contextKeyService: IContextKeyService, @inject(VSCodeThemeService) themeService: IThemeService) {
60
72
  super(contextKeyService, themeService);
61
73
  }
@@ -63,7 +75,7 @@ export class MonacoEditorService extends StandaloneCodeEditorService {
63
75
  /**
64
76
  * Monaco active editor is either focused or last focused editor.
65
77
  */
66
- override getActiveCodeEditor(): StandaloneCodeEditor | null {
78
+ override getActiveCodeEditor(): ICodeEditor | null {
67
79
  let editor = MonacoEditor.getCurrent(this.editors);
68
80
  if (!editor && CustomEditorWidget.is(this.shell.activeWidget)) {
69
81
  const model = this.shell.activeWidget.modelRef.object;
@@ -73,8 +85,17 @@ export class MonacoEditorService extends StandaloneCodeEditorService {
73
85
  }
74
86
  const candidate = editor?.getControl();
75
87
  // Since we extend a private super class, we have to check that the thing that matches the public interface also matches the private expectations the superclass.
88
+ if (candidate instanceof StandaloneCodeEditor) {
89
+ return candidate;
90
+ }
91
+ for (const activeEditorProvider of this.activeMonacoEditorContribution.getContributions()) {
92
+ const activeEditor = activeEditorProvider.getActiveEditor();
93
+ if (activeEditor) {
94
+ return activeEditor;
95
+ }
96
+ }
76
97
  /* eslint-disable-next-line no-null/no-null */
77
- return candidate instanceof StandaloneCodeEditor ? candidate : null;
98
+ return null;
78
99
  }
79
100
 
80
101
  override async openCodeEditor(input: IResourceEditorInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
@@ -25,13 +25,13 @@ import {
25
25
  WidgetStatusBarContribution
26
26
  } from '@theia/core/lib/browser';
27
27
  import { TextEditorProvider, DiffNavigatorProvider, TextEditor } from '@theia/editor/lib/browser';
28
- import { MonacoEditorProvider, MonacoEditorFactory } from './monaco-editor-provider';
28
+ import { MonacoEditorProvider, MonacoEditorFactory, SaveParticipant } from './monaco-editor-provider';
29
29
  import { MonacoEditorMenuContribution } from './monaco-menu';
30
30
  import { MonacoEditorCommandHandlers } from './monaco-command';
31
31
  import { MonacoKeybindingContribution } from './monaco-keybinding';
32
32
  import { MonacoLanguages } from './monaco-languages';
33
33
  import { MonacoWorkspace } from './monaco-workspace';
34
- import { MonacoEditorService, MonacoEditorServiceFactory, VSCodeContextKeyService, VSCodeThemeService } from './monaco-editor-service';
34
+ import { ActiveMonacoEditorContribution, MonacoEditorService, MonacoEditorServiceFactory, VSCodeContextKeyService, VSCodeThemeService } from './monaco-editor-service';
35
35
  import { MonacoTextModelService, MonacoEditorModelFactory, MonacoEditorModelFilter } from './monaco-text-model-service';
36
36
  import { MonacoContextMenuService } from './monaco-context-menu';
37
37
  import { MonacoOutlineContribution } from './monaco-outline-contribution';
@@ -79,6 +79,7 @@ import { ActiveMonacoUndoRedoHandler, FocusedMonacoUndoRedoHandler } from './mon
79
79
  import { ILogService } from '@theia/monaco-editor-core/esm/vs/platform/log/common/log';
80
80
  import { DefaultContentHoverWidgetPatcher } from './default-content-hover-widget-patcher';
81
81
  import { MonacoWorkspaceContextService } from './monaco-workspace-context-service';
82
+ import { MonacoCodeActionSaveParticipant } from './monaco-code-action-save-participant';
82
83
 
83
84
  export default new ContainerModule((bind, unbind, isBound, rebind) => {
84
85
  bind(MonacoThemingService).toSelf().inSingletonScope();
@@ -91,6 +92,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
91
92
  bind(FrontendApplicationContribution).toService(MonacoFrontendApplicationContribution);
92
93
  bind(StylingParticipant).toService(MonacoFrontendApplicationContribution);
93
94
 
95
+ bind(MonacoCodeActionSaveParticipant).toSelf().inSingletonScope();
96
+ bind(SaveParticipant).toService(MonacoCodeActionSaveParticipant);
97
+
94
98
  bind(MonacoToProtocolConverter).toSelf().inSingletonScope();
95
99
  bind(ProtocolToMonacoConverter).toSelf().inSingletonScope();
96
100
 
@@ -107,6 +111,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
107
111
  bind(MonacoConfigurationService).toDynamicValue(({ container }) => createMonacoConfigurationService(container)).inSingletonScope();
108
112
 
109
113
  bind(MonacoBulkEditService).toSelf().inSingletonScope();
114
+ bindContributionProvider(bind, ActiveMonacoEditorContribution);
110
115
  bind(MonacoEditorServiceFactory).toFactory((context: interfaces.Context) => (contextKeyService: IContextKeyService, themeService: IThemeService) => {
111
116
  const child = context.container.createChild();
112
117
  child.bind(VSCodeContextKeyService).toConstantValue(contextKeyService);
@@ -121,6 +126,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
121
126
  bindContributionProvider(bind, MonacoEditorFactory);
122
127
  bindContributionProvider(bind, MonacoEditorModelFactory);
123
128
  bindContributionProvider(bind, MonacoEditorModelFilter);
129
+ bindContributionProvider(bind, SaveParticipant);
124
130
  bind(MonacoCommandService).toSelf().inTransientScope();
125
131
 
126
132
  bind(TextEditorProvider).toProvider(context =>
@@ -40,16 +40,17 @@ export class MonacoEditorMenuContribution implements MenuContribution {
40
40
  ) { }
41
41
 
42
42
  registerMenus(registry: MenuModelRegistry): void {
43
+ registry.registerSubmenu(EDITOR_CONTEXT_MENU, 'Editor Context Menu');
43
44
  for (const item of MenuRegistry.getMenuItems(MenuId.EditorContext)) {
44
45
  if (!isIMenuItem(item)) {
45
46
  continue;
46
47
  }
47
48
  const commandId = this.commands.validate(item.command.id);
48
49
  if (commandId) {
49
- const menuPath = [...EDITOR_CONTEXT_MENU, (item.group || '')];
50
- const coreId = MonacoCommands.COMMON_ACTIONS.get(commandId);
51
- if (!(coreId && registry.getMenu(menuPath).children.some(it => it.id === coreId))) {
52
- // Don't add additional actions if the item is already registered with a core ID.
50
+ const nodeId = MonacoCommands.COMMON_ACTIONS.get(commandId) || commandId;
51
+ const menuPath = item.group ? [...EDITOR_CONTEXT_MENU, item.group] : EDITOR_CONTEXT_MENU;
52
+ if (registry.getMenuNode([...menuPath, nodeId])) {
53
+ // Don't add additional actions if the item is already registered.
53
54
  registry.registerMenuAction(menuPath, this.buildMenuAction(commandId, item));
54
55
  }
55
56
  }
@@ -23,7 +23,7 @@ import { Emitter } from '@theia/core/lib/common/event';
23
23
  import { FileSystemPreferences } from '@theia/filesystem/lib/browser';
24
24
  import { EditorManager, EditorPreferences } from '@theia/editor/lib/browser';
25
25
  import { MonacoTextModelService } from './monaco-text-model-service';
26
- import { WillSaveMonacoModelEvent, MonacoEditorModel, MonacoModelContentChangedEvent } from './monaco-editor-model';
26
+ import { MonacoEditorModel, MonacoModelContentChangedEvent } from './monaco-editor-model';
27
27
  import { MonacoEditor } from './monaco-editor';
28
28
  import { ProblemManager } from '@theia/markers/lib/browser';
29
29
  import { ArrayUtils } from '@theia/core/lib/common/types';
@@ -101,9 +101,6 @@ export class MonacoWorkspace {
101
101
  protected readonly onDidChangeTextDocumentEmitter = new Emitter<MonacoModelContentChangedEvent>();
102
102
  readonly onDidChangeTextDocument = this.onDidChangeTextDocumentEmitter.event;
103
103
 
104
- protected readonly onWillSaveTextDocumentEmitter = new Emitter<WillSaveMonacoModelEvent>();
105
- readonly onWillSaveTextDocument = this.onWillSaveTextDocumentEmitter.event;
106
-
107
104
  protected readonly onDidSaveTextDocumentEmitter = new Emitter<MonacoEditorModel>();
108
105
  readonly onDidSaveTextDocument = this.onDidSaveTextDocumentEmitter.event;
109
106
 
@@ -160,7 +157,6 @@ export class MonacoWorkspace {
160
157
  });
161
158
  model.onDidChangeContent(event => this.fireDidChangeContent(event));
162
159
  model.onDidSaveModel(() => this.fireDidSave(model));
163
- model.onWillSaveModel(event => this.fireWillSave(event));
164
160
  model.onDirtyChanged(() => this.openEditorIfDirty(model));
165
161
  model.onDispose(() => this.fireDidClose(model));
166
162
  }
@@ -177,10 +173,6 @@ export class MonacoWorkspace {
177
173
  this.onDidChangeTextDocumentEmitter.fire(event);
178
174
  }
179
175
 
180
- protected fireWillSave(event: WillSaveMonacoModelEvent): void {
181
- this.onWillSaveTextDocumentEmitter.fire(event);
182
- }
183
-
184
176
  protected fireDidSave(model: MonacoEditorModel): void {
185
177
  this.onDidSaveTextDocumentEmitter.fire(model);
186
178
  }