@theia/plugin-ext 1.70.0-next.61 → 1.70.0-next.71

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 (67) hide show
  1. package/lib/common/plugin-api-rpc.d.ts +25 -2
  2. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  3. package/lib/common/plugin-api-rpc.js.map +1 -1
  4. package/lib/main/browser/languages-main.d.ts.map +1 -1
  5. package/lib/main/browser/languages-main.js +6 -1
  6. package/lib/main/browser/languages-main.js.map +1 -1
  7. package/lib/main/browser/menus/menus-contribution-handler.d.ts.map +1 -1
  8. package/lib/main/browser/menus/menus-contribution-handler.js +2 -0
  9. package/lib/main/browser/menus/menus-contribution-handler.js.map +1 -1
  10. package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts.map +1 -1
  11. package/lib/main/browser/menus/plugin-menu-command-adapter.js +3 -0
  12. package/lib/main/browser/menus/plugin-menu-command-adapter.js.map +1 -1
  13. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +3 -2
  14. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
  15. package/lib/main/browser/menus/vscode-theia-menu-mappings.js +10 -2
  16. package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
  17. package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
  18. package/lib/main/browser/plugin-contribution-handler.js +6 -1
  19. package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
  20. package/lib/main/browser/plugin-ext-widget.d.ts.map +1 -1
  21. package/lib/main/browser/plugin-ext-widget.js +1 -1
  22. package/lib/main/browser/plugin-ext-widget.js.map +1 -1
  23. package/lib/main/browser/scm-main.d.ts +4 -2
  24. package/lib/main/browser/scm-main.d.ts.map +1 -1
  25. package/lib/main/browser/scm-main.js +8 -4
  26. package/lib/main/browser/scm-main.js.map +1 -1
  27. package/lib/main/browser/text-editor-main.d.ts +2 -0
  28. package/lib/main/browser/text-editor-main.d.ts.map +1 -1
  29. package/lib/main/browser/text-editor-main.js +3 -0
  30. package/lib/main/browser/text-editor-main.js.map +1 -1
  31. package/lib/main/browser/text-editors-main.d.ts +4 -0
  32. package/lib/main/browser/text-editors-main.d.ts.map +1 -1
  33. package/lib/main/browser/text-editors-main.js +121 -0
  34. package/lib/main/browser/text-editors-main.js.map +1 -1
  35. package/lib/plugin/command-registry.d.ts.map +1 -1
  36. package/lib/plugin/command-registry.js +11 -0
  37. package/lib/plugin/command-registry.js.map +1 -1
  38. package/lib/plugin/env.d.ts +5 -0
  39. package/lib/plugin/env.d.ts.map +1 -1
  40. package/lib/plugin/env.js +7 -0
  41. package/lib/plugin/env.js.map +1 -1
  42. package/lib/plugin/plugin-context.d.ts.map +1 -1
  43. package/lib/plugin/plugin-context.js +3 -2
  44. package/lib/plugin/plugin-context.js.map +1 -1
  45. package/lib/plugin/scm.d.ts +2 -1
  46. package/lib/plugin/scm.d.ts.map +1 -1
  47. package/lib/plugin/scm.js +20 -3
  48. package/lib/plugin/scm.js.map +1 -1
  49. package/lib/plugin/text-editors.d.ts +2 -2
  50. package/lib/plugin/text-editors.d.ts.map +1 -1
  51. package/lib/plugin/text-editors.js.map +1 -1
  52. package/package.json +29 -29
  53. package/src/common/plugin-api-rpc.ts +21 -2
  54. package/src/main/browser/languages-main.ts +6 -1
  55. package/src/main/browser/menus/menus-contribution-handler.ts +2 -0
  56. package/src/main/browser/menus/plugin-menu-command-adapter.ts +3 -0
  57. package/src/main/browser/menus/vscode-theia-menu-mappings.ts +12 -2
  58. package/src/main/browser/plugin-contribution-handler.ts +6 -1
  59. package/src/main/browser/plugin-ext-widget.tsx +1 -1
  60. package/src/main/browser/scm-main.ts +13 -6
  61. package/src/main/browser/text-editor-main.ts +4 -0
  62. package/src/main/browser/text-editors-main.ts +132 -0
  63. package/src/plugin/command-registry.ts +12 -1
  64. package/src/plugin/env.ts +8 -0
  65. package/src/plugin/plugin-context.ts +3 -2
  66. package/src/plugin/scm.ts +26 -4
  67. package/src/plugin/text-editors.ts +2 -2
package/package.json CHANGED
@@ -1,38 +1,38 @@
1
1
  {
2
2
  "name": "@theia/plugin-ext",
3
- "version": "1.70.0-next.61+d13baea89",
3
+ "version": "1.70.0-next.71+7ece9ea62",
4
4
  "description": "Theia - Plugin Extension",
5
5
  "main": "lib/common/index.js",
6
6
  "typings": "lib/common/index.d.ts",
7
7
  "dependencies": {
8
- "@theia/ai-mcp": "1.70.0-next.61+d13baea89",
9
- "@theia/bulk-edit": "1.70.0-next.61+d13baea89",
10
- "@theia/callhierarchy": "1.70.0-next.61+d13baea89",
11
- "@theia/console": "1.70.0-next.61+d13baea89",
12
- "@theia/core": "1.70.0-next.61+d13baea89",
13
- "@theia/debug": "1.70.0-next.61+d13baea89",
14
- "@theia/editor": "1.70.0-next.61+d13baea89",
15
- "@theia/editor-preview": "1.70.0-next.61+d13baea89",
16
- "@theia/file-search": "1.70.0-next.61+d13baea89",
17
- "@theia/filesystem": "1.70.0-next.61+d13baea89",
18
- "@theia/markers": "1.70.0-next.61+d13baea89",
19
- "@theia/messages": "1.70.0-next.61+d13baea89",
20
- "@theia/monaco": "1.70.0-next.61+d13baea89",
8
+ "@theia/ai-mcp": "1.70.0-next.71+7ece9ea62",
9
+ "@theia/bulk-edit": "1.70.0-next.71+7ece9ea62",
10
+ "@theia/callhierarchy": "1.70.0-next.71+7ece9ea62",
11
+ "@theia/console": "1.70.0-next.71+7ece9ea62",
12
+ "@theia/core": "1.70.0-next.71+7ece9ea62",
13
+ "@theia/debug": "1.70.0-next.71+7ece9ea62",
14
+ "@theia/editor": "1.70.0-next.71+7ece9ea62",
15
+ "@theia/editor-preview": "1.70.0-next.71+7ece9ea62",
16
+ "@theia/file-search": "1.70.0-next.71+7ece9ea62",
17
+ "@theia/filesystem": "1.70.0-next.71+7ece9ea62",
18
+ "@theia/markers": "1.70.0-next.71+7ece9ea62",
19
+ "@theia/messages": "1.70.0-next.71+7ece9ea62",
20
+ "@theia/monaco": "1.70.0-next.71+7ece9ea62",
21
21
  "@theia/monaco-editor-core": "1.108.201",
22
- "@theia/navigator": "1.70.0-next.61+d13baea89",
23
- "@theia/notebook": "1.70.0-next.61+d13baea89",
24
- "@theia/output": "1.70.0-next.61+d13baea89",
25
- "@theia/plugin": "1.70.0-next.61+d13baea89",
26
- "@theia/preferences": "1.70.0-next.61+d13baea89",
27
- "@theia/scm": "1.70.0-next.61+d13baea89",
28
- "@theia/search-in-workspace": "1.70.0-next.61+d13baea89",
29
- "@theia/task": "1.70.0-next.61+d13baea89",
30
- "@theia/terminal": "1.70.0-next.61+d13baea89",
31
- "@theia/test": "1.70.0-next.61+d13baea89",
32
- "@theia/timeline": "1.70.0-next.61+d13baea89",
33
- "@theia/typehierarchy": "1.70.0-next.61+d13baea89",
34
- "@theia/variable-resolver": "1.70.0-next.61+d13baea89",
35
- "@theia/workspace": "1.70.0-next.61+d13baea89",
22
+ "@theia/navigator": "1.70.0-next.71+7ece9ea62",
23
+ "@theia/notebook": "1.70.0-next.71+7ece9ea62",
24
+ "@theia/output": "1.70.0-next.71+7ece9ea62",
25
+ "@theia/plugin": "1.70.0-next.71+7ece9ea62",
26
+ "@theia/preferences": "1.70.0-next.71+7ece9ea62",
27
+ "@theia/scm": "1.70.0-next.71+7ece9ea62",
28
+ "@theia/search-in-workspace": "1.70.0-next.71+7ece9ea62",
29
+ "@theia/task": "1.70.0-next.71+7ece9ea62",
30
+ "@theia/terminal": "1.70.0-next.71+7ece9ea62",
31
+ "@theia/test": "1.70.0-next.71+7ece9ea62",
32
+ "@theia/timeline": "1.70.0-next.71+7ece9ea62",
33
+ "@theia/typehierarchy": "1.70.0-next.71+7ece9ea62",
34
+ "@theia/variable-resolver": "1.70.0-next.71+7ece9ea62",
35
+ "@theia/workspace": "1.70.0-next.71+7ece9ea62",
36
36
  "@types/mime": "^2.0.1",
37
37
  "@vscode/debugprotocol": "^1.51.0",
38
38
  "@vscode/proxy-agent": "^0.13.2",
@@ -97,5 +97,5 @@
97
97
  "nyc": {
98
98
  "extends": "../../configs/nyc.json"
99
99
  },
100
- "gitHead": "d13baea893dbdfe4c1df677a9b00b11289e71fa7"
100
+ "gitHead": "7ece9ea6295b73ee1ac962e29d416cebf578d380"
101
101
  }
@@ -1037,7 +1037,7 @@ export interface DecorationsMain {
1037
1037
  }
1038
1038
 
1039
1039
  export interface ScmMain {
1040
- $registerSourceControl(sourceControlHandle: number, id: string, label: string, rootUri?: UriComponents): Promise<void>;
1040
+ $registerSourceControl(sourceControlHandle: number, id: string, label: string, rootUri?: UriComponents, parentHandle?: number): Promise<void>;
1041
1041
  $updateSourceControl(sourceControlHandle: number, features: SourceControlProviderFeatures): Promise<void>;
1042
1042
  $unregisterSourceControl(sourceControlHandle: number): Promise<void>;
1043
1043
 
@@ -1062,6 +1062,7 @@ export interface SourceControlProviderFeatures {
1062
1062
  commitTemplate?: string;
1063
1063
  acceptInputCommand?: Command;
1064
1064
  statusBarCommands?: Command[];
1065
+ contextValue?: string;
1065
1066
  }
1066
1067
 
1067
1068
  export interface SourceControlGroupFeatures {
@@ -1248,9 +1249,22 @@ export interface TextEditorPositionData {
1248
1249
  [id: string]: EditorPosition;
1249
1250
  }
1250
1251
 
1252
+ export interface TextEditorDiffInformationDto {
1253
+ readonly documentVersion: number;
1254
+ readonly original: UriComponents | undefined;
1255
+ readonly modified: UriComponents;
1256
+ readonly changes: readonly {
1257
+ readonly original: { readonly startLineNumber: number; readonly endLineNumberExclusive: number };
1258
+ readonly modified: { readonly startLineNumber: number; readonly endLineNumberExclusive: number };
1259
+ readonly kind: number;
1260
+ }[];
1261
+ readonly isStale: boolean;
1262
+ }
1263
+
1251
1264
  export interface TextEditorsExt {
1252
1265
  $acceptEditorPropertiesChanged(id: string, props: EditorChangedPropertiesData): void;
1253
1266
  $acceptEditorPositionData(data: TextEditorPositionData): void;
1267
+ $acceptEditorDiffInformation(id: string, diffInformation: TextEditorDiffInformationDto[] | undefined): void;
1254
1268
  }
1255
1269
 
1256
1270
  export interface SingleEditOperation {
@@ -1502,8 +1516,13 @@ export interface OutputChannelRegistryMain {
1502
1516
 
1503
1517
  export type CharacterPair = [string, string];
1504
1518
 
1519
+ export interface LineCommentRule {
1520
+ comment: string;
1521
+ noIndent?: boolean;
1522
+ }
1523
+
1505
1524
  export interface CommentRule {
1506
- lineComment?: string;
1525
+ lineComment?: string | LineCommentRule;
1507
1526
  blockComment?: CharacterPair;
1508
1527
  }
1509
1528
 
@@ -152,8 +152,13 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
152
152
  }
153
153
 
154
154
  $setLanguageConfiguration(handle: number, languageId: string, configuration: SerializedLanguageConfiguration): void {
155
+ const comments = configuration.comments;
155
156
  const config: monaco.languages.LanguageConfiguration = {
156
- comments: configuration.comments,
157
+ comments: comments ? {
158
+ // @monaco-uplift: Monaco doesn't support LineCommentRule yet, extract the string
159
+ lineComment: comments.lineComment && typeof comments.lineComment === 'object' ? comments.lineComment.comment : comments.lineComment,
160
+ blockComment: comments.blockComment,
161
+ } : undefined,
157
162
  brackets: configuration.brackets,
158
163
  wordPattern: reviveRegExp(configuration.wordPattern),
159
164
  indentationRules: reviveIndentationRule(configuration.indentationRules),
@@ -22,6 +22,7 @@ import { MenuModelRegistry } from '@theia/core/lib/common';
22
22
  import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
23
23
  import { DeployedPlugin, IconUrl, Menu } from '../../../common';
24
24
  import { ScmWidget } from '@theia/scm/lib/browser/scm-widget';
25
+ import { ScmRepositoriesWidget, SCM_SOURCE_CONTROL_TITLE_MENU } from '@theia/scm/lib/browser/scm-repositories-widget';
25
26
  import { KeybindingRegistry, QuickCommandService, codicon } from '@theia/core/lib/browser';
26
27
  import {
27
28
  CodeEditorWidgetUtil, codeToTheiaMappings, ContributionPoint,
@@ -61,6 +62,7 @@ export class MenusContributionPointHandler {
61
62
  isVisible: widget => CodeEditorWidgetUtil.is(widget)
62
63
  });
63
64
  this.tabBarToolbar.registerMenuDelegate(PLUGIN_SCM_TITLE_MENU, widget => widget instanceof ScmWidget);
65
+ this.tabBarToolbar.registerMenuDelegate(SCM_SOURCE_CONTROL_TITLE_MENU, widget => widget instanceof ScmRepositoriesWidget);
64
66
  this.tabBarToolbar.registerMenuDelegate(PLUGIN_VIEW_TITLE_MENU, widget => !CodeEditorWidgetUtil.is(widget));
65
67
  }
66
68
 
@@ -70,6 +70,9 @@ export class PluginMenuCommandAdapter {
70
70
  ['scm/resourceGroup/context', toScmArgs],
71
71
  ['scm/resourceState/context', toScmArgs],
72
72
  ['scm/title', () => [this.toScmArg(this.scmService.selectedRepository)]],
73
+ ['scm/sourceControl', toScmArgs],
74
+ ['scm/sourceControl/context', toScmArgs],
75
+ ['scm/sourceControl/title', () => [this.toScmArg(this.scmService.selectedRepository)]],
73
76
  ['testing/message/context', toTestMessageArgs],
74
77
  ['testing/profiles/context', noArgs],
75
78
  ['scm/change/title', (...args) => this.toScmChangeArgs(...args)],
@@ -26,6 +26,9 @@ import { EditorWidget, EDITOR_CONTEXT_MENU, EDITOR_CONTENT_MENU } from '@theia/e
26
26
  import { NAVIGATOR_CONTEXT_MENU } from '@theia/navigator/lib/browser/navigator-contribution';
27
27
  import { ScmTreeWidget } from '@theia/scm/lib/browser/scm-tree-widget';
28
28
  import { PLUGIN_SCM_CHANGE_TITLE_MENU } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-widget';
29
+ import {
30
+ SCM_SOURCE_CONTROL_CONTEXT_MENU, SCM_SOURCE_CONTROL_MENU, SCM_SOURCE_CONTROL_TITLE_MENU, SCM_TITLE_MENU
31
+ } from '@theia/scm/lib/browser/scm-repositories-widget';
29
32
  import { TIMELINE_ITEM_CONTEXT_MENU } from '@theia/timeline/lib/browser/timeline-tree-widget';
30
33
  import { COMMENT_CONTEXT, COMMENT_THREAD_CONTEXT, COMMENT_TITLE } from '../comments/comment-thread-widget';
31
34
  import { VIEW_ITEM_CONTEXT_MENU } from '../view/tree-view-widget';
@@ -37,7 +40,8 @@ import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-con
37
40
 
38
41
  export const PLUGIN_EDITOR_TITLE_MENU = ['plugin_editor/title'];
39
42
  export const PLUGIN_EDITOR_TITLE_RUN_MENU = ['plugin_editor/title/run'];
40
- export const PLUGIN_SCM_TITLE_MENU = ['plugin_scm/title'];
43
+ /** @deprecated Use SCM_TITLE_MENU from @theia/scm/lib/browser/scm-repositories-widget */
44
+ export const PLUGIN_SCM_TITLE_MENU = SCM_TITLE_MENU;
41
45
  export const PLUGIN_VIEW_TITLE_MENU = ['plugin_view/title'];
42
46
 
43
47
  export const implementedVSCodeContributionPoints = [
@@ -58,6 +62,9 @@ export const implementedVSCodeContributionPoints = [
58
62
  'scm/resourceFolder/context',
59
63
  'scm/resourceGroup/context',
60
64
  'scm/resourceState/context',
65
+ 'scm/sourceControl',
66
+ 'scm/sourceControl/context',
67
+ 'scm/sourceControl/title',
61
68
  'scm/title',
62
69
  'timeline/item/context',
63
70
  'testing/item/context',
@@ -93,7 +100,10 @@ export const codeToTheiaMappings = new Map<string, MenuPath[]>([
93
100
  ['scm/resourceFolder/context', [ScmTreeWidget.RESOURCE_FOLDER_CONTEXT_MENU]],
94
101
  ['scm/resourceGroup/context', [ScmTreeWidget.RESOURCE_GROUP_CONTEXT_MENU]],
95
102
  ['scm/resourceState/context', [ScmTreeWidget.RESOURCE_CONTEXT_MENU]],
96
- ['scm/title', [PLUGIN_SCM_TITLE_MENU]],
103
+ ['scm/sourceControl', [SCM_SOURCE_CONTROL_MENU]],
104
+ ['scm/sourceControl/context', [SCM_SOURCE_CONTROL_CONTEXT_MENU]],
105
+ ['scm/sourceControl/title', [SCM_SOURCE_CONTROL_TITLE_MENU]],
106
+ ['scm/title', [SCM_TITLE_MENU]],
97
107
  ['testing/item/context', [TEST_VIEW_CONTEXT_MENU]],
98
108
  ['testing/message/context', [TEST_RUNS_CONTEXT_MENU]],
99
109
  ['testing/profiles/context', [PLUGIN_TEST_VIEW_TITLE_MENU]],
@@ -209,11 +209,16 @@ export class PluginContributionHandler {
209
209
  }
210
210
  const langConfiguration = lang.configuration;
211
211
  if (langConfiguration) {
212
+ const comments = langConfiguration.comments;
212
213
  pushContribution(`language.${lang.id}.configuration`, () => monaco.languages.setLanguageConfiguration(lang.id, {
213
214
  wordPattern: this.createRegex(langConfiguration.wordPattern),
214
215
  autoClosingPairs: langConfiguration.autoClosingPairs,
215
216
  brackets: langConfiguration.brackets,
216
- comments: langConfiguration.comments,
217
+ // @monaco-uplift: Monaco doesn't support LineCommentRule yet, extract the string
218
+ comments: comments ? {
219
+ lineComment: comments.lineComment && typeof comments.lineComment === 'object' ? comments.lineComment.comment : comments.lineComment,
220
+ blockComment: comments.blockComment,
221
+ } : undefined,
217
222
  folding: this.convertFolding(langConfiguration.folding),
218
223
  surroundingPairs: langConfiguration.surroundingPairs,
219
224
  indentationRules: this.convertIndentationRules(langConfiguration.indentationRules),
@@ -26,7 +26,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
26
26
  import { codicon } from '@theia/core/lib/browser';
27
27
  import { nls } from '@theia/core/lib/common';
28
28
 
29
- export const PLUGINS_LABEL = nls.localize('theia/plugin-ext/plugins', 'Plugins');
29
+ export const PLUGINS_LABEL = nls.localizeByDefault('Plugins');
30
30
 
31
31
  @injectable()
32
32
  export class PluginWidget extends ReactWidget {
@@ -120,6 +120,8 @@ export class PluginScmProvider implements ScmProvider {
120
120
 
121
121
  private features: SourceControlProviderFeatures = {};
122
122
 
123
+ get providerContextValue(): string | undefined { return this.features.contextValue; }
124
+
123
125
  get handle(): number { return this._handle; }
124
126
  get label(): string { return this._label; }
125
127
  get rootUri(): string { return this._rootUri ? this._rootUri.toString() : ''; }
@@ -164,7 +166,8 @@ export class PluginScmProvider implements ScmProvider {
164
166
  private readonly _contextValue: string,
165
167
  private readonly _label: string,
166
168
  private readonly _rootUri: vscodeURI | undefined,
167
- private disposables: DisposableCollection
169
+ private disposables: DisposableCollection,
170
+ readonly parentHandle?: number
168
171
  ) { }
169
172
 
170
173
  updateSourceControl(features: SourceControlProviderFeatures): void {
@@ -330,17 +333,21 @@ export class ScmMainImpl implements ScmMain {
330
333
  this.disposables.dispose();
331
334
  }
332
335
 
333
- async $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined): Promise<void> {
334
- const provider = new PluginScmProvider(this.proxy, this.colors, this.sharedStyle, handle, id, label, rootUri ? vscodeURI.revive(rootUri) : undefined, this.disposables);
336
+ async $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined, parentHandle?: number): Promise<void> {
337
+ const provider = new PluginScmProvider(
338
+ this.proxy, this.colors, this.sharedStyle, handle, id, label,
339
+ rootUri ? vscodeURI.revive(rootUri) : undefined, this.disposables, parentHandle
340
+ );
341
+ const parentRepo = parentHandle !== undefined ? this.repositories.get(parentHandle) : undefined;
335
342
  const repository = this.scmService.registerScmProvider(provider, {
336
343
  input: {
337
344
  validator: async value => {
338
345
  const result = await this.proxy.$validateInput(handle, value, value.length);
339
346
  return result && { message: result[0], type: result[1] };
340
347
  }
341
- }
342
- }
343
- );
348
+ },
349
+ parentRootUri: parentRepo?.provider.rootUri
350
+ });
344
351
  this.repositories.set(handle, repository);
345
352
 
346
353
  const disposables = new DisposableCollection(
@@ -147,6 +147,10 @@ export class TextEditorMain implements Disposable {
147
147
  return this.editor.diffInformation;
148
148
  }
149
149
 
150
+ getDiffEditor(): MonacoDiffEditor | undefined {
151
+ return this.editor instanceof MonacoDiffEditor ? this.editor : undefined;
152
+ }
153
+
150
154
  setSelections(selections: Selection[]): void {
151
155
  if (this.editor) {
152
156
  this.editor.getControl().setSelections(selections);
@@ -49,6 +49,11 @@ import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/stan
49
49
  import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService';
50
50
  import { type ILineChange } from '@theia/monaco-editor-core/esm/vs/editor/common/diff/legacyLinesDiffComputer';
51
51
  import { ArrayUtils, URI } from '@theia/core';
52
+ import { DiffUris } from '@theia/core/lib/browser/diff-uris';
53
+ import { TextEditorChangeKind } from '../../plugin/types-impl';
54
+ import { Change } from '@theia/scm/lib/browser/dirty-diff/diff-computer';
55
+ import { DirtyDiffUpdate } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-decorator';
56
+ import { ScmDecorationsService } from '@theia/scm/lib/browser/decorations/scm-decorations-service';
52
57
  import { toNotebookWorspaceEdit } from './notebooks/notebooks-main';
53
58
  import { interfaces } from '@theia/core/shared/inversify';
54
59
  import { NotebookService } from '@theia/notebook/lib/browser';
@@ -62,6 +67,7 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable {
62
67
 
63
68
  private readonly bulkEditService: MonacoBulkEditService;
64
69
  private readonly notebookService: NotebookService;
70
+ private readonly scmDecorationsService: ScmDecorationsService;
65
71
 
66
72
  constructor(
67
73
  private readonly editorsAndDocuments: EditorsAndDocumentsMain,
@@ -73,10 +79,12 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable {
73
79
 
74
80
  this.bulkEditService = container.get(MonacoBulkEditService);
75
81
  this.notebookService = container.get(NotebookService);
82
+ this.scmDecorationsService = container.get(ScmDecorationsService);
76
83
 
77
84
  this.toDispose.push(editorsAndDocuments);
78
85
  this.toDispose.push(editorsAndDocuments.onTextEditorAdd(editors => editors.forEach(this.onTextEditorAdd, this)));
79
86
  this.toDispose.push(editorsAndDocuments.onTextEditorRemove(editors => editors.forEach(this.onTextEditorRemove, this)));
87
+ this.toDispose.push(this.scmDecorationsService.onDirtyDiffUpdate(update => this.onDirtyDiffUpdate(update)));
80
88
  }
81
89
 
82
90
  dispose(): void {
@@ -93,6 +101,130 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable {
93
101
  );
94
102
  this.editorsToDispose.set(id, toDispose);
95
103
  this.toDispose.push(toDispose);
104
+
105
+ const diffEditor = editor.getDiffEditor();
106
+ if (diffEditor) {
107
+ const pushDiffEditorInfo = () => this.pushDiffEditorDiffInformation(id, editor);
108
+ toDispose.push(diffEditor.diffEditor.onDidUpdateDiff(pushDiffEditorInfo));
109
+ pushDiffEditorInfo();
110
+ }
111
+ }
112
+
113
+ private onDirtyDiffUpdate(update: DirtyDiffUpdate): void {
114
+ const editorUri = update.editor.uri.toString();
115
+ const editorIds = this.findEditorIdsByUri(editorUri, true);
116
+ if (editorIds.length === 0) {
117
+ return;
118
+ }
119
+
120
+ const originalUri = update.previousRevisionUri?.toComponents();
121
+
122
+ const changes = update.changes.map(change => {
123
+ let kind: TextEditorChangeKind;
124
+ if (Change.isAddition(change)) {
125
+ kind = TextEditorChangeKind.Addition;
126
+ } else if (Change.isRemoval(change)) {
127
+ kind = TextEditorChangeKind.Deletion;
128
+ } else {
129
+ kind = TextEditorChangeKind.Modification;
130
+ }
131
+ return {
132
+ original: {
133
+ startLineNumber: change.previousRange.start + 1,
134
+ endLineNumberExclusive: change.previousRange.end + 1
135
+ },
136
+ modified: {
137
+ startLineNumber: change.currentRange.start + 1,
138
+ endLineNumberExclusive: change.currentRange.end + 1
139
+ },
140
+ kind
141
+ };
142
+ });
143
+
144
+ // Push diff information to all editors with this URI (regular editors and diff editor modified sides).
145
+ for (const editorId of editorIds) {
146
+ const editor = this.editorsAndDocuments.getEditor(editorId);
147
+ if (!editor) {
148
+ continue;
149
+ }
150
+ const model = editor.getModel();
151
+ const modifiedUri = URI.fromComponents(model.uri).toComponents();
152
+ this.proxy.$acceptEditorDiffInformation(editorId, [{
153
+ documentVersion: model.getVersionId(),
154
+ original: originalUri,
155
+ modified: modifiedUri,
156
+ changes,
157
+ isStale: false
158
+ }]);
159
+ }
160
+ }
161
+
162
+ private pushDiffEditorDiffInformation(id: string, editor: TextEditorMain): void {
163
+ const diffEditor = editor.getDiffEditor();
164
+ if (!diffEditor) {
165
+ return;
166
+ }
167
+
168
+ let originalUri: UriComponents | undefined;
169
+ let modifiedUri: UriComponents;
170
+
171
+ try {
172
+ const [left, right] = DiffUris.decode(diffEditor.uri);
173
+ originalUri = left.toComponents();
174
+ modifiedUri = right.toComponents();
175
+ } catch {
176
+ // Not a valid DiffUri; fall back to model URIs
177
+ originalUri = new URI(diffEditor.originalModel.uri).toComponents();
178
+ modifiedUri = new URI(diffEditor.modifiedModel.uri).toComponents();
179
+ }
180
+
181
+ const lineChanges = diffEditor.diffInformation;
182
+
183
+ const changes = lineChanges.map(change => {
184
+ let kind: TextEditorChangeKind;
185
+ if (change.originalEndLineNumber === 0) {
186
+ kind = TextEditorChangeKind.Addition;
187
+ } else if (change.modifiedEndLineNumber === 0) {
188
+ kind = TextEditorChangeKind.Deletion;
189
+ } else {
190
+ kind = TextEditorChangeKind.Modification;
191
+ }
192
+
193
+ // ILineChange uses 1-based lines where 0 means empty range.
194
+ // TextEditorLineRange uses 1-based startLineNumber and endLineNumberExclusive.
195
+ const toLineRange = (start: number, end: number) => end === 0
196
+ ? { startLineNumber: start + 1, endLineNumberExclusive: start + 1 }
197
+ : { startLineNumber: start, endLineNumberExclusive: end + 1 };
198
+
199
+ return {
200
+ original: toLineRange(change.originalStartLineNumber, change.originalEndLineNumber),
201
+ modified: toLineRange(change.modifiedStartLineNumber, change.modifiedEndLineNumber),
202
+ kind
203
+ };
204
+ });
205
+
206
+ const model = editor.getModel();
207
+ this.proxy.$acceptEditorDiffInformation(id, [{
208
+ documentVersion: model.getVersionId(),
209
+ original: originalUri,
210
+ modified: modifiedUri,
211
+ changes,
212
+ isStale: false
213
+ }]);
214
+ }
215
+
216
+ private findEditorIdsByUri(uri: string, excludeDiffEditors = false): string[] {
217
+ const ids: string[] = [];
218
+ for (const id of this.editorsToDispose.keys()) {
219
+ const editor = this.editorsAndDocuments.getEditor(id);
220
+ if (editor && editor.getModel().uri.toString() === uri) {
221
+ if (excludeDiffEditors && editor.getDiffEditor()) {
222
+ continue;
223
+ }
224
+ ids.push(id);
225
+ }
226
+ }
227
+ return ids;
96
228
  }
97
229
 
98
230
  private onTextEditorRemove(id: string): void {
@@ -22,10 +22,11 @@ import * as theia from '@theia/plugin';
22
22
  import * as model from '../common/plugin-api-rpc-model';
23
23
  import { CommandRegistryExt, PLUGIN_RPC_CONTEXT as Ext, CommandRegistryMain } from '../common/plugin-api-rpc';
24
24
  import { RPCProtocol } from '../common/rpc-protocol';
25
- import { Disposable } from './types-impl';
25
+ import { Disposable, URI } from './types-impl';
26
26
  import { DisposableCollection } from '@theia/core';
27
27
  import { KnownCommands } from './known-commands';
28
28
  import { ArgumentProcessor } from '../common/commands';
29
+ import { isUriComponents } from './type-converters';
29
30
 
30
31
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
32
  export type Handler = <T>(...args: any[]) => T | PromiseLike<T | undefined>;
@@ -42,6 +43,16 @@ export class CommandRegistryImpl implements CommandRegistryExt {
42
43
  this.proxy = rpc.getProxy(Ext.COMMAND_REGISTRY_MAIN);
43
44
  this.argumentProcessors = [];
44
45
  this.commandsConverter = new CommandsConverter(this);
46
+ this.registerArgumentProcessor({
47
+ processArgument: arg => {
48
+ // Revive URI-like plain objects that lost their class identity during RPC/JSON serialization
49
+ // (e.g. command arguments from markdown links parsed by CommandOpenHandler)
50
+ if (isUriComponents(arg) && !(arg instanceof URI)) {
51
+ return URI.revive(arg);
52
+ }
53
+ return arg;
54
+ }
55
+ });
45
56
  }
46
57
 
47
58
  get converter(): CommandsConverter {
package/src/plugin/env.ts CHANGED
@@ -108,6 +108,14 @@ export abstract class EnvExtImpl {
108
108
 
109
109
  abstract get isNewAppInstall(): boolean;
110
110
 
111
+ /**
112
+ * @stubbed
113
+ * Portable mode detection is not yet communicated to the plugin host.
114
+ */
115
+ get isAppPortable(): boolean {
116
+ return false;
117
+ }
118
+
111
119
  get appHost(): string {
112
120
  return this.host;
113
121
  }
@@ -930,6 +930,7 @@ export function createAPIFactory(
930
930
  get appHost(): string { return envExt.appHost; },
931
931
  get language(): string { return envExt.language; },
932
932
  get isNewAppInstall(): boolean { return envExt.isNewAppInstall; },
933
+ get isAppPortable(): boolean { return envExt.isAppPortable; },
933
934
  get isTelemetryEnabled(): boolean { return telemetryExt.isTelemetryEnabled; },
934
935
  get onDidChangeTelemetryEnabled(): theia.Event<boolean> {
935
936
  return telemetryExt.onDidChangeTelemetryEnabled;
@@ -1285,8 +1286,8 @@ export function createAPIFactory(
1285
1286
  throw new Error('Input box not found!');
1286
1287
  }
1287
1288
  },
1288
- createSourceControl(id: string, label: string, rootUri?: URI): theia.SourceControl {
1289
- return createAPIObject(scmExt.createSourceControl(plugin, id, label, rootUri));
1289
+ createSourceControl(id: string, label: string, rootUri?: URI, iconPath?: theia.IconPath, parent?: theia.SourceControl): theia.SourceControl {
1290
+ return createAPIObject(scmExt.createSourceControl(plugin, id, label, rootUri, iconPath, parent));
1290
1291
  }
1291
1292
  };
1292
1293
 
package/src/plugin/scm.ts CHANGED
@@ -676,12 +676,23 @@ class SourceControlImpl implements theia.SourceControl {
676
676
  private readonly onDidChangeSelectionEmitter = new Emitter<boolean>();
677
677
  readonly onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
678
678
 
679
+ private _contextValue: string | undefined = undefined;
680
+
681
+ get contextValue(): string | undefined {
682
+ return this._contextValue;
683
+ }
684
+
685
+ set contextValue(contextValue: string | undefined) {
686
+ this._contextValue = contextValue;
687
+ this.proxy.$updateSourceControl(this.handle, { contextValue });
688
+ }
689
+
679
690
  private readonly onDidDisposeEmitter = new Emitter<void>();
680
691
  readonly onDidDispose = this.onDidDisposeEmitter.event;
681
692
 
682
693
  readonly onDidDisposeParent: Event<void>;
683
694
 
684
- private handle: number = SourceControlImpl.handlePool++;
695
+ readonly handle: number = SourceControlImpl.handlePool++;
685
696
 
686
697
  constructor(
687
698
  private plugin: Plugin,
@@ -694,7 +705,7 @@ class SourceControlImpl implements theia.SourceControl {
694
705
  _parent?: SourceControlImpl
695
706
  ) {
696
707
  this.inputBox = new ScmInputBoxImpl(plugin, this.proxy, this.handle);
697
- this.proxy.$registerSourceControl(this.handle, _id, _label, _rootUri);
708
+ this.proxy.$registerSourceControl(this.handle, _id, _label, _rootUri, _parent?.handle);
698
709
  this.onDidDisposeParent = _parent ? _parent.onDidDispose : Event.None;
699
710
  }
700
711
 
@@ -824,9 +835,11 @@ export class ScmExtImpl implements ScmExt {
824
835
  });
825
836
  }
826
837
 
827
- createSourceControl(extension: Plugin, id: string, label: string, rootUri: theia.Uri | undefined): theia.SourceControl {
838
+ createSourceControl(extension: Plugin, id: string, label: string, rootUri: theia.Uri | undefined,
839
+ iconPath?: theia.IconPath, parent?: theia.SourceControl): theia.SourceControl {
828
840
  const handle = ScmExtImpl.handlePool++;
829
- const sourceControl = new SourceControlImpl(extension, this.proxy, this.commands, id, label, rootUri);
841
+ const parentImpl = parent ? this.findSourceControlImpl(parent) : undefined;
842
+ const sourceControl = new SourceControlImpl(extension, this.proxy, this.commands, id, label, rootUri, iconPath, parentImpl);
830
843
  this.sourceControls.set(handle, sourceControl);
831
844
 
832
845
  const sourceControls = this.sourceControlsByExtension.get(extension.model.id) || [];
@@ -836,6 +849,15 @@ export class ScmExtImpl implements ScmExt {
836
849
  return sourceControl;
837
850
  }
838
851
 
852
+ private findSourceControlImpl(apiObject: theia.SourceControl): SourceControlImpl | undefined {
853
+ for (const impl of this.sourceControls.values()) {
854
+ if (impl.id === apiObject.id && impl.rootUri?.toString() === apiObject.rootUri?.toString()) {
855
+ return impl;
856
+ }
857
+ }
858
+ return undefined;
859
+ }
860
+
839
861
  getLastInputBox(extension: Plugin): ScmInputBoxImpl | undefined {
840
862
  const sourceControls = this.sourceControlsByExtension.get(extension.model.id);
841
863
  const sourceControl = sourceControls && sourceControls[sourceControls.length - 1];
@@ -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 { TextEditorsExt, EditorChangedPropertiesData, TextEditorPositionData, TextEditorsMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc';
17
+ import { TextEditorsExt, EditorChangedPropertiesData, TextEditorPositionData, TextEditorsMain, PLUGIN_RPC_CONTEXT, TextEditorDiffInformationDto } from '../common/plugin-api-rpc';
18
18
  import { RPCProtocol } from '../common/rpc-protocol';
19
19
  import * as theia from '@theia/plugin';
20
20
  import { Emitter, Event } from '@theia/core/lib/common/event';
@@ -120,7 +120,7 @@ export class TextEditorsExtImpl implements TextEditorsExt {
120
120
  return activeEditor.getDiffInformation();
121
121
  }
122
122
 
123
- $acceptEditorDiffInformation(id: string, diffInformation: theia.TextEditorDiffInformation[] | undefined): void {
123
+ $acceptEditorDiffInformation(id: string, diffInformation: TextEditorDiffInformationDto[] | undefined): void {
124
124
  const textEditor = this.editorsAndDocuments.getEditor(id);
125
125
  if (!textEditor) {
126
126
  throw new Error('unknown text editor');