@theia/editor 1.59.0-next.62 → 1.59.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.
@@ -14,12 +14,11 @@
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, postConstruct, inject } from '@theia/core/shared/inversify';
17
+ import { injectable, postConstruct, inject, named } from '@theia/core/shared/inversify';
18
18
  import URI from '@theia/core/lib/common/uri';
19
- import { RecursivePartial, Emitter, Event, MaybePromise, CommandService, nls } from '@theia/core/lib/common';
19
+ import { RecursivePartial, Emitter, Event, MaybePromise, CommandService, nls, ContributionProvider, Prioritizeable, Disposable } from '@theia/core/lib/common';
20
20
  import {
21
- WidgetOpenerOptions, NavigatableWidgetOpenHandler, NavigatableWidgetOptions, Widget, PreferenceService, CommonCommands, getDefaultHandler,
22
- defaultHandlerPriority
21
+ WidgetOpenerOptions, NavigatableWidgetOpenHandler, NavigatableWidgetOptions, Widget, PreferenceService, CommonCommands, getDefaultHandler, defaultHandlerPriority
23
22
  } from '@theia/core/lib/browser';
24
23
  import { EditorWidget } from './editor-widget';
25
24
  import { Range, Position, Location, TextEditor } from './editor';
@@ -33,7 +32,13 @@ export interface WidgetId {
33
32
  export interface EditorOpenerOptions extends WidgetOpenerOptions {
34
33
  selection?: RecursivePartial<Range>;
35
34
  preview?: boolean;
36
- counter?: number
35
+ counter?: number;
36
+ }
37
+
38
+ export const EditorSelectionResolver = Symbol('EditorSelectionResolver');
39
+ export interface EditorSelectionResolver {
40
+ priority?: number;
41
+ resolveSelection(widget: EditorWidget, options: EditorOpenerOptions, uri?: URI): Promise<RecursivePartial<Range> | undefined>;
37
42
  }
38
43
 
39
44
  @injectable()
@@ -60,9 +65,19 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
60
65
  @inject(CommandService) protected readonly commands: CommandService;
61
66
  @inject(PreferenceService) protected readonly preferenceService: PreferenceService;
62
67
 
68
+ @inject(ContributionProvider) @named(EditorSelectionResolver)
69
+ protected readonly resolverContributions: ContributionProvider<EditorSelectionResolver>;
70
+ protected selectionResolvers: EditorSelectionResolver[] = [];
71
+
63
72
  @postConstruct()
64
73
  protected override init(): void {
65
74
  super.init();
75
+
76
+ this.selectionResolvers = Prioritizeable.prioritizeAllSync(
77
+ this.resolverContributions.getContributions(),
78
+ resolver => resolver.priority ?? 0
79
+ ).map(p => p.value);
80
+
66
81
  this.shell.onDidChangeActiveWidget(() => this.updateActiveEditor());
67
82
  this.shell.onDidChangeCurrentWidget(() => this.updateCurrentEditor());
68
83
  this.shell.onDidDoubleClickMainArea(() =>
@@ -91,6 +106,26 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
91
106
  this.updateCurrentEditor();
92
107
  }
93
108
 
109
+ /**
110
+ * Registers a dynamic selection resolver.
111
+ * The resolver is added to the sorted list of selection resolvers and can later be disposed to remove it.
112
+ *
113
+ * @param resolver The selection resolver to register.
114
+ * @returns A Disposable that unregisters the resolver when disposed.
115
+ */
116
+ public registerSelectionResolver(resolver: EditorSelectionResolver): Disposable {
117
+ this.selectionResolvers.push(resolver);
118
+ this.selectionResolvers.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
119
+ return {
120
+ dispose: () => {
121
+ const index = this.selectionResolvers.indexOf(resolver);
122
+ if (index !== -1) {
123
+ this.selectionResolvers.splice(index, 1);
124
+ }
125
+ }
126
+ };
127
+ }
128
+
94
129
  override getByUri(uri: URI, options?: EditorOpenerOptions): Promise<EditorWidget | undefined> {
95
130
  return this.getWidget(uri, options);
96
131
  }
@@ -101,22 +136,24 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
101
136
 
102
137
  protected override tryGetPendingWidget(uri: URI, options?: EditorOpenerOptions): MaybePromise<EditorWidget> | undefined {
103
138
  const editorPromise = super.tryGetPendingWidget(uri, options);
104
- if (editorPromise) {
105
- // Reveal selection before attachment to manage nav stack. (https://github.com/eclipse-theia/theia/issues/8955)
106
- if (!(editorPromise instanceof Widget)) {
107
- editorPromise.then(editor => this.revealSelection(editor, options, uri));
108
- } else {
109
- this.revealSelection(editorPromise, options, uri);
110
- }
139
+ if (!editorPromise) {
140
+ return editorPromise;
111
141
  }
112
- return editorPromise;
142
+
143
+ // Reveal selection before attachment to manage nav stack. (https://github.com/eclipse-theia/theia/issues/8955)
144
+ if (!(editorPromise instanceof Widget)) {
145
+ return editorPromise.then(editor => this.revealSelection(editor, options, uri)).then(() => editorPromise);
146
+ } else {
147
+ return this.revealSelection(editorPromise, options, uri).then(() => editorPromise);
148
+ }
149
+
113
150
  }
114
151
 
115
152
  protected override async getWidget(uri: URI, options?: EditorOpenerOptions): Promise<EditorWidget | undefined> {
116
153
  const editor = await super.getWidget(uri, options);
117
154
  if (editor) {
118
155
  // Reveal selection before attachment to manage nav stack. (https://github.com/eclipse-theia/theia/issues/8955)
119
- this.revealSelection(editor, options, uri);
156
+ await this.revealSelection(editor, options, uri);
120
157
  }
121
158
  return editor;
122
159
  }
@@ -124,7 +161,7 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
124
161
  protected override async getOrCreateWidget(uri: URI, options?: EditorOpenerOptions): Promise<EditorWidget> {
125
162
  const editor = await super.getOrCreateWidget(uri, options);
126
163
  // Reveal selection before attachment to manage nav stack. (https://github.com/eclipse-theia/theia/issues/8955)
127
- this.revealSelection(editor, options, uri);
164
+ await this.revealSelection(editor, options, uri);
128
165
  return editor;
129
166
  }
130
167
 
@@ -240,13 +277,17 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
240
277
  return this.open(uri, splitOptions);
241
278
  }
242
279
 
243
- protected revealSelection(widget: EditorWidget, input?: EditorOpenerOptions, uri?: URI): void {
280
+ protected async revealSelection(widget: EditorWidget, input?: EditorOpenerOptions, uri?: URI): Promise<void> {
244
281
  let inputSelection = input?.selection;
282
+ if (!inputSelection) {
283
+ inputSelection = await this.resolveSelection(widget, input ?? {}, uri);
284
+ }
285
+ // this logic could be moved into a 'EditorSelectionResolver'
245
286
  if (!inputSelection && uri) {
287
+ // support file:///some/file.js#73,84
288
+ // support file:///some/file.js#L73
246
289
  const match = /^L?(\d+)(?:,(\d+))?/.exec(uri.fragment);
247
290
  if (match) {
248
- // support file:///some/file.js#73,84
249
- // support file:///some/file.js#L73
250
291
  inputSelection = {
251
292
  start: {
252
293
  line: parseInt(match[1]) - 1,
@@ -256,22 +297,36 @@ export class EditorManager extends NavigatableWidgetOpenHandler<EditorWidget> {
256
297
  }
257
298
  }
258
299
  if (inputSelection) {
259
- const editor = widget.editor;
260
300
  const selection = this.getSelection(widget, inputSelection);
301
+ const editor = widget.editor;
261
302
  if (Position.is(selection)) {
262
303
  editor.cursor = selection;
263
304
  editor.revealPosition(selection);
264
305
  } else if (Range.is(selection)) {
265
306
  editor.cursor = selection.end;
266
- editor.selection = {
267
- ...selection,
268
- direction: 'ltr'
269
- };
307
+ editor.selection = { ...selection, direction: 'ltr' };
270
308
  editor.revealRange(selection);
271
309
  }
272
310
  }
273
311
  }
274
312
 
313
+ protected async resolveSelection(widget: EditorWidget, options: EditorOpenerOptions, uri?: URI): Promise<RecursivePartial<Range> | undefined> {
314
+ if (options.selection) {
315
+ return options.selection;
316
+ }
317
+ for (const resolver of this.selectionResolvers) {
318
+ try {
319
+ const selection = await resolver.resolveSelection(widget, options, uri);
320
+ if (selection) {
321
+ return selection;
322
+ }
323
+ } catch (error) {
324
+ console.error(error);
325
+ }
326
+ }
327
+ return undefined;
328
+ }
329
+
275
330
  protected getSelection(widget: EditorWidget, selection: RecursivePartial<Range>): Range | Position | undefined {
276
331
  const { start, end } = selection;
277
332
  if (Position.is(start)) {
@@ -40,7 +40,7 @@ const codeActionsContributionSchema: PreferenceSchema['properties'] = {
40
40
  properties: {
41
41
  'source.fixAll': {
42
42
  type: 'boolean',
43
- description: nls.localizeByDefault('Controls whether auto fix action should be run on file save.')
43
+ description: nls.localizeByDefault('Controls whether \'{0}\' actions should be run on file save.', 'quickfix')
44
44
  }
45
45
  },
46
46
  additionalProperties: {
@@ -53,7 +53,7 @@ const codeActionsContributionSchema: PreferenceSchema['properties'] = {
53
53
  }
54
54
  ],
55
55
  default: {},
56
- markdownDescription: nls.localizeByDefault('Run Code Actions for the editor on save. Code Actions must be specified and the editor must not be shutting down. Example: `"source.organizeImports": "explicit" `'),
56
+ markdownDescription: nls.localizeByDefault('Run Code Actions for the editor on save. Code Actions must be specified and the editor must not be shutting down. When {0} is set to `afterDelay`, Code Actions will only be run when the file is saved explicitly. Example: `"source.organizeImports": "explicit" `'),
57
57
  scope: 'language-overridable',
58
58
  }
59
59
  };
@@ -68,7 +68,7 @@ interface CodeActionsContributionProperties {
68
68
  const fileContributionSchema: PreferenceSchema['properties'] = {
69
69
  'editor.formatOnSave': {
70
70
  'type': 'boolean',
71
- 'description': nls.localizeByDefault('Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down.'),
71
+ 'description': nls.localizeByDefault('Format a file on save. A formatter must be available and the editor must not be shutting down. When {0} is set to `afterDelay`, the file will only be formatted when saved explicitly.'),
72
72
  'scope': PreferenceScope.fromString('language-overridable'),
73
73
  },
74
74
  'editor.formatOnSaveMode': {