@theia/plugin-ext 1.76.0-next.4 → 1.76.0-next.42

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 (93) hide show
  1. package/lib/common/plugin-api-rpc.d.ts +14 -0
  2. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  3. package/lib/common/plugin-api-rpc.js +1 -1
  4. package/lib/common/plugin-api-rpc.js.map +1 -1
  5. package/lib/hosted/browser/hosted-plugin.d.ts +2 -0
  6. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  7. package/lib/hosted/browser/hosted-plugin.js +8 -2
  8. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  9. package/lib/hosted/browser/worker/worker-main.js +3 -1
  10. package/lib/hosted/browser/worker/worker-main.js.map +1 -1
  11. package/lib/hosted/browser/worker/worker-plugin-module.d.ts.map +1 -1
  12. package/lib/hosted/browser/worker/worker-plugin-module.js +2 -0
  13. package/lib/hosted/browser/worker/worker-plugin-module.js.map +1 -1
  14. package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
  15. package/lib/hosted/node/plugin-host-module.js +2 -0
  16. package/lib/hosted/node/plugin-host-module.js.map +1 -1
  17. package/lib/hosted/node/plugin-host-rpc.d.ts +3 -0
  18. package/lib/hosted/node/plugin-host-rpc.d.ts.map +1 -1
  19. package/lib/hosted/node/plugin-host-rpc.js +9 -3
  20. package/lib/hosted/node/plugin-host-rpc.js.map +1 -1
  21. package/lib/main/browser/editors-and-documents-main.d.ts +7 -0
  22. package/lib/main/browser/editors-and-documents-main.d.ts.map +1 -1
  23. package/lib/main/browser/editors-and-documents-main.js +19 -9
  24. package/lib/main/browser/editors-and-documents-main.js.map +1 -1
  25. package/lib/main/browser/editors-and-documents-main.spec.d.ts +2 -0
  26. package/lib/main/browser/editors-and-documents-main.spec.d.ts.map +1 -0
  27. package/lib/main/browser/editors-and-documents-main.spec.js +117 -0
  28. package/lib/main/browser/editors-and-documents-main.spec.js.map +1 -0
  29. package/lib/main/browser/languages-main.d.ts +7 -0
  30. package/lib/main/browser/languages-main.d.ts.map +1 -1
  31. package/lib/main/browser/languages-main.js +31 -9
  32. package/lib/main/browser/languages-main.js.map +1 -1
  33. package/lib/main/browser/languages-main.spec.d.ts +2 -0
  34. package/lib/main/browser/languages-main.spec.d.ts.map +1 -0
  35. package/lib/main/browser/languages-main.spec.js +82 -0
  36. package/lib/main/browser/languages-main.spec.js.map +1 -0
  37. package/lib/main/browser/main-context.d.ts.map +1 -1
  38. package/lib/main/browser/main-context.js +3 -0
  39. package/lib/main/browser/main-context.js.map +1 -1
  40. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +5 -0
  41. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
  42. package/lib/main/browser/menus/vscode-theia-menu-mappings.js +5 -0
  43. package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
  44. package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
  45. package/lib/main/browser/plugin-ext-frontend-module.js +1 -0
  46. package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
  47. package/lib/main/browser/telemetry-main.d.ts +20 -0
  48. package/lib/main/browser/telemetry-main.d.ts.map +1 -0
  49. package/lib/main/browser/telemetry-main.js +43 -0
  50. package/lib/main/browser/telemetry-main.js.map +1 -0
  51. package/lib/main/browser/telemetry-main.spec.d.ts +2 -0
  52. package/lib/main/browser/telemetry-main.spec.d.ts.map +1 -0
  53. package/lib/main/browser/telemetry-main.spec.js +66 -0
  54. package/lib/main/browser/telemetry-main.spec.js.map +1 -0
  55. package/lib/main/browser/webview/webview-context-keys.d.ts +3 -2
  56. package/lib/main/browser/webview/webview-context-keys.d.ts.map +1 -1
  57. package/lib/main/browser/webview/webview-context-keys.js +16 -2
  58. package/lib/main/browser/webview/webview-context-keys.js.map +1 -1
  59. package/lib/plugin/plugin-context.d.ts +2 -1
  60. package/lib/plugin/plugin-context.d.ts.map +1 -1
  61. package/lib/plugin/plugin-context.js +2 -3
  62. package/lib/plugin/plugin-context.js.map +1 -1
  63. package/lib/plugin/plugin-manager.d.ts +2 -0
  64. package/lib/plugin/plugin-manager.d.ts.map +1 -1
  65. package/lib/plugin/plugin-manager.js +7 -0
  66. package/lib/plugin/plugin-manager.js.map +1 -1
  67. package/lib/plugin/telemetry-ext.d.ts +23 -7
  68. package/lib/plugin/telemetry-ext.d.ts.map +1 -1
  69. package/lib/plugin/telemetry-ext.js +60 -33
  70. package/lib/plugin/telemetry-ext.js.map +1 -1
  71. package/lib/plugin/telemetry-ext.spec.js +121 -16
  72. package/lib/plugin/telemetry-ext.spec.js.map +1 -1
  73. package/package.json +32 -31
  74. package/src/common/plugin-api-rpc.ts +15 -1
  75. package/src/hosted/browser/hosted-plugin.ts +7 -2
  76. package/src/hosted/browser/worker/worker-main.ts +4 -1
  77. package/src/hosted/browser/worker/worker-plugin-module.ts +2 -0
  78. package/src/hosted/node/plugin-host-module.ts +2 -0
  79. package/src/hosted/node/plugin-host-rpc.ts +10 -4
  80. package/src/main/browser/editors-and-documents-main.spec.ts +140 -0
  81. package/src/main/browser/editors-and-documents-main.ts +19 -7
  82. package/src/main/browser/languages-main.spec.ts +101 -0
  83. package/src/main/browser/languages-main.ts +31 -9
  84. package/src/main/browser/main-context.ts +4 -0
  85. package/src/main/browser/menus/vscode-theia-menu-mappings.ts +5 -0
  86. package/src/main/browser/plugin-ext-frontend-module.ts +2 -1
  87. package/src/main/browser/telemetry-main.spec.ts +77 -0
  88. package/src/main/browser/telemetry-main.ts +46 -0
  89. package/src/main/browser/webview/webview-context-keys.ts +20 -4
  90. package/src/plugin/plugin-context.ts +3 -2
  91. package/src/plugin/plugin-manager.ts +6 -0
  92. package/src/plugin/telemetry-ext.spec.ts +158 -17
  93. package/src/plugin/telemetry-ext.ts +63 -33
@@ -18,34 +18,139 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  const chai = require("chai");
19
19
  const telemetry_ext_1 = require("./telemetry-ext");
20
20
  const expect = chai.expect;
21
- describe('TelemetryLogger', () => {
21
+ describe('TelemetryExtImpl', () => {
22
22
  const sender = {
23
23
  sendEventData: () => { },
24
24
  sendErrorData: () => { }
25
25
  };
26
- it('reflects the current telemetry enabled state', () => {
26
+ const expectations = {
27
+ off: { isTelemetryEnabled: false, isUsageEnabled: false, isErrorsEnabled: false },
28
+ crash: { isTelemetryEnabled: false, isUsageEnabled: false, isErrorsEnabled: false },
29
+ error: { isTelemetryEnabled: false, isUsageEnabled: false, isErrorsEnabled: true },
30
+ all: { isTelemetryEnabled: true, isUsageEnabled: true, isErrorsEnabled: true }
31
+ };
32
+ for (const level of ['off', 'crash', 'error', 'all']) {
33
+ const expected = expectations[level];
34
+ it(`derives the enablement states from telemetry level '${level}'`, () => {
35
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
36
+ telemetry.$onDidChangeTelemetryLevel(level);
37
+ const logger = telemetry.createTelemetryLogger(sender);
38
+ expect(telemetry.level).to.equal(level);
39
+ expect(telemetry.isTelemetryEnabled).to.equal(expected.isTelemetryEnabled);
40
+ expect(logger.isUsageEnabled).to.equal(expected.isUsageEnabled);
41
+ expect(logger.isErrorsEnabled).to.equal(expected.isErrorsEnabled);
42
+ });
43
+ it(`applies telemetry level '${level}' to loggers created before the level arrived`, () => {
44
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
45
+ const logger = telemetry.createTelemetryLogger(sender);
46
+ telemetry.$onDidChangeTelemetryLevel(level);
47
+ expect(logger.isUsageEnabled).to.equal(expected.isUsageEnabled);
48
+ expect(logger.isErrorsEnabled).to.equal(expected.isErrorsEnabled);
49
+ });
50
+ }
51
+ it('only fires onDidChangeTelemetryEnabled when the boolean it reports changes', () => {
52
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
53
+ const fired = [];
54
+ telemetry.onDidChangeTelemetryEnabled(enabled => fired.push(enabled));
55
+ telemetry.$onDidChangeTelemetryLevel('crash');
56
+ telemetry.$onDidChangeTelemetryLevel('error');
57
+ telemetry.$onDidChangeTelemetryLevel('all');
58
+ telemetry.$onDidChangeTelemetryLevel('off');
59
+ expect(fired).to.deep.equal([true, false]);
60
+ });
61
+ it('fires onDidChangeEnableStates once per transition that changes an enablement state', () => {
27
62
  const telemetry = new telemetry_ext_1.TelemetryExtImpl();
28
63
  const logger = telemetry.createTelemetryLogger(sender);
29
- expect(logger.telemetryEnabled).to.equal(false);
30
- expect(logger.isUsageEnabled).to.equal(false);
31
- expect(logger.isErrorsEnabled).to.equal(false);
32
- telemetry.isTelemetryEnabled = true;
33
- expect(logger.telemetryEnabled).to.equal(true);
34
- expect(logger.isUsageEnabled).to.equal(true);
35
- expect(logger.isErrorsEnabled).to.equal(true);
36
- telemetry.isTelemetryEnabled = false;
37
- expect(logger.telemetryEnabled).to.equal(false);
64
+ let changes = 0;
65
+ logger.onDidChangeEnableStates(() => changes++);
66
+ telemetry.$onDidChangeTelemetryLevel('crash'); // off -> crash: both stay false
67
+ expect(changes).to.equal(0);
68
+ telemetry.$onDidChangeTelemetryLevel('error'); // errors become enabled
69
+ expect(changes).to.equal(1);
70
+ telemetry.$onDidChangeTelemetryLevel('all'); // usage becomes enabled
71
+ expect(changes).to.equal(2);
72
+ telemetry.$onDidChangeTelemetryLevel('off');
73
+ expect(changes).to.equal(3);
74
+ });
75
+ it('stops following the level once the logger is disposed', () => {
76
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
77
+ const logger = telemetry.createTelemetryLogger(sender);
78
+ let changes = 0;
79
+ logger.onDidChangeEnableStates(() => changes++);
80
+ logger.dispose();
81
+ telemetry.$onDidChangeTelemetryLevel('all');
38
82
  expect(logger.isUsageEnabled).to.equal(false);
39
- expect(logger.isErrorsEnabled).to.equal(false);
83
+ logger.isUsageEnabled = true;
84
+ expect(changes).to.equal(0);
40
85
  });
41
- it('emits enable-state changes when telemetry is toggled', () => {
86
+ it('logs rather than propagates a sender that fails to flush on disposal', async () => {
87
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
88
+ const logged = [];
89
+ const consoleError = console.error;
90
+ console.error = (...args) => { logged.push(args); };
91
+ try {
92
+ telemetry.createTelemetryLogger({ ...sender, flush: () => { throw new Error('boom'); } }).dispose();
93
+ telemetry.createTelemetryLogger({ ...sender, flush: () => Promise.reject(new Error('boom')) }).dispose();
94
+ await new Promise(resolve => setTimeout(resolve, 0));
95
+ }
96
+ finally {
97
+ console.error = consoleError;
98
+ }
99
+ expect(logged).to.have.lengthOf(2);
100
+ });
101
+ it('ignores a repeated telemetry level', () => {
42
102
  const telemetry = new telemetry_ext_1.TelemetryExtImpl();
43
103
  const logger = telemetry.createTelemetryLogger(sender);
44
104
  let changes = 0;
45
105
  logger.onDidChangeEnableStates(() => changes++);
46
- telemetry.isTelemetryEnabled = true;
47
- telemetry.isTelemetryEnabled = false;
48
- expect(changes).to.equal(2);
106
+ telemetry.$onDidChangeTelemetryLevel('all');
107
+ telemetry.$onDidChangeTelemetryLevel('all');
108
+ expect(changes).to.equal(1);
109
+ });
110
+ });
111
+ describe('TelemetryLogger', () => {
112
+ function createSender() {
113
+ const events = [];
114
+ const errors = [];
115
+ return {
116
+ events,
117
+ errors,
118
+ sendEventData: (eventName) => events.push(eventName),
119
+ sendErrorData: (error) => errors.push(error)
120
+ };
121
+ }
122
+ it('delivers errors but suppresses usage at telemetry level \'error\'', () => {
123
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
124
+ telemetry.$onDidChangeTelemetryLevel('error');
125
+ const sender = createSender();
126
+ const logger = telemetry.createTelemetryLogger(sender);
127
+ logger.logUsage('usage-event');
128
+ logger.logError('error-event');
129
+ logger.logError(new Error('boom'));
130
+ expect(sender.events).to.deep.equal(['error-event']);
131
+ expect(sender.errors).to.have.lengthOf(1);
132
+ });
133
+ it('suppresses usage that a plugin re-enables while the telemetry level forbids it', () => {
134
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
135
+ telemetry.$onDidChangeTelemetryLevel('off');
136
+ const sender = createSender();
137
+ const logger = telemetry.createTelemetryLogger(sender);
138
+ logger.isUsageEnabled = true;
139
+ logger.isErrorsEnabled = true;
140
+ logger.logUsage('usage-event');
141
+ logger.logError('error-event');
142
+ expect(sender.events).to.be.empty;
143
+ });
144
+ it('refuses to let a plugin assign the telemetry level of its logger', () => {
145
+ const telemetry = new telemetry_ext_1.TelemetryExtImpl();
146
+ telemetry.$onDidChangeTelemetryLevel('off');
147
+ const sender = createSender();
148
+ const logger = telemetry.createTelemetryLogger(sender);
149
+ expect(Reflect.set(logger, 'level', 'all')).to.be.false;
150
+ logger.logUsage('usage-event');
151
+ expect(logger.level).to.equal('off');
152
+ expect(logger.isUsageEnabled).to.be.false;
153
+ expect(sender.events).to.be.empty;
49
154
  });
50
155
  });
51
156
  //# sourceMappingURL=telemetry-ext.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-ext.spec.js","sourceRoot":"","sources":["../../src/plugin/telemetry-ext.spec.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,wCAAwC;AACxC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;AAEhF,6BAA6B;AAC7B,mDAAmD;AAEnD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;AAE3B,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC7B,MAAM,MAAM,GAAG;QACX,aAAa,EAAE,GAAG,EAAE,GAAG,CAAC;QACxB,aAAa,EAAE,GAAG,EAAE,GAAG,CAAC;KAC3B,CAAC;IAEF,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACpD,MAAM,SAAS,GAAG,IAAI,gCAAgB,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,SAAS,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAEvD,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAE/C,SAAS,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAEpC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE9C,SAAS,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAErC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC5D,MAAM,SAAS,GAAG,IAAI,gCAAgB,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,SAAS,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,MAAM,CAAC,uBAAuB,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAEhD,SAAS,CAAC,kBAAkB,GAAG,IAAI,CAAC;QACpC,SAAS,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAErC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"telemetry-ext.spec.js","sourceRoot":"","sources":["../../src/plugin/telemetry-ext.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,40 +1,41 @@
1
1
  {
2
2
  "name": "@theia/plugin-ext",
3
- "version": "1.76.0-next.4+21b9b5342",
3
+ "version": "1.76.0-next.42+b0f9e63a6",
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-core": "1.76.0-next.4+21b9b5342",
9
- "@theia/ai-mcp": "1.76.0-next.4+21b9b5342",
10
- "@theia/bulk-edit": "1.76.0-next.4+21b9b5342",
11
- "@theia/callhierarchy": "1.76.0-next.4+21b9b5342",
12
- "@theia/console": "1.76.0-next.4+21b9b5342",
13
- "@theia/core": "1.76.0-next.4+21b9b5342",
14
- "@theia/debug": "1.76.0-next.4+21b9b5342",
15
- "@theia/editor": "1.76.0-next.4+21b9b5342",
16
- "@theia/editor-preview": "1.76.0-next.4+21b9b5342",
17
- "@theia/file-search": "1.76.0-next.4+21b9b5342",
18
- "@theia/filesystem": "1.76.0-next.4+21b9b5342",
19
- "@theia/markers": "1.76.0-next.4+21b9b5342",
20
- "@theia/messages": "1.76.0-next.4+21b9b5342",
21
- "@theia/monaco": "1.76.0-next.4+21b9b5342",
8
+ "@theia/ai-core": "1.76.0-next.42+b0f9e63a6",
9
+ "@theia/ai-mcp": "1.76.0-next.42+b0f9e63a6",
10
+ "@theia/bulk-edit": "1.76.0-next.42+b0f9e63a6",
11
+ "@theia/callhierarchy": "1.76.0-next.42+b0f9e63a6",
12
+ "@theia/console": "1.76.0-next.42+b0f9e63a6",
13
+ "@theia/core": "1.76.0-next.42+b0f9e63a6",
14
+ "@theia/debug": "1.76.0-next.42+b0f9e63a6",
15
+ "@theia/editor": "1.76.0-next.42+b0f9e63a6",
16
+ "@theia/editor-preview": "1.76.0-next.42+b0f9e63a6",
17
+ "@theia/file-search": "1.76.0-next.42+b0f9e63a6",
18
+ "@theia/filesystem": "1.76.0-next.42+b0f9e63a6",
19
+ "@theia/markers": "1.76.0-next.42+b0f9e63a6",
20
+ "@theia/messages": "1.76.0-next.42+b0f9e63a6",
21
+ "@theia/monaco": "1.76.0-next.42+b0f9e63a6",
22
22
  "@theia/monaco-editor-core": "1.108.201",
23
- "@theia/navigator": "1.76.0-next.4+21b9b5342",
24
- "@theia/notebook": "1.76.0-next.4+21b9b5342",
25
- "@theia/output": "1.76.0-next.4+21b9b5342",
26
- "@theia/plugin": "1.76.0-next.4+21b9b5342",
27
- "@theia/plugin-utils": "1.76.0-next.4+21b9b5342",
28
- "@theia/preferences": "1.76.0-next.4+21b9b5342",
29
- "@theia/scm": "1.76.0-next.4+21b9b5342",
30
- "@theia/search-in-workspace": "1.76.0-next.4+21b9b5342",
31
- "@theia/task": "1.76.0-next.4+21b9b5342",
32
- "@theia/terminal": "1.76.0-next.4+21b9b5342",
33
- "@theia/test": "1.76.0-next.4+21b9b5342",
34
- "@theia/timeline": "1.76.0-next.4+21b9b5342",
35
- "@theia/typehierarchy": "1.76.0-next.4+21b9b5342",
36
- "@theia/variable-resolver": "1.76.0-next.4+21b9b5342",
37
- "@theia/workspace": "1.76.0-next.4+21b9b5342",
23
+ "@theia/navigator": "1.76.0-next.42+b0f9e63a6",
24
+ "@theia/notebook": "1.76.0-next.42+b0f9e63a6",
25
+ "@theia/output": "1.76.0-next.42+b0f9e63a6",
26
+ "@theia/plugin": "1.76.0-next.42+b0f9e63a6",
27
+ "@theia/plugin-utils": "1.76.0-next.42+b0f9e63a6",
28
+ "@theia/preferences": "1.76.0-next.42+b0f9e63a6",
29
+ "@theia/scm": "1.76.0-next.42+b0f9e63a6",
30
+ "@theia/search-in-workspace": "1.76.0-next.42+b0f9e63a6",
31
+ "@theia/task": "1.76.0-next.42+b0f9e63a6",
32
+ "@theia/telemetry": "1.76.0-next.42+b0f9e63a6",
33
+ "@theia/terminal": "1.76.0-next.42+b0f9e63a6",
34
+ "@theia/test": "1.76.0-next.42+b0f9e63a6",
35
+ "@theia/timeline": "1.76.0-next.42+b0f9e63a6",
36
+ "@theia/typehierarchy": "1.76.0-next.42+b0f9e63a6",
37
+ "@theia/variable-resolver": "1.76.0-next.42+b0f9e63a6",
38
+ "@theia/workspace": "1.76.0-next.42+b0f9e63a6",
38
39
  "@types/mime": "^2.0.3",
39
40
  "@vscode/debugprotocol": "^1.68.0",
40
41
  "@vscode/proxy-agent": "^0.13.2",
@@ -99,5 +100,5 @@
99
100
  "nyc": {
100
101
  "extends": "../../configs/nyc.json"
101
102
  },
102
- "gitHead": "21b9b534224c55152fca0b67af7f8a8283dad2bd"
103
+ "gitHead": "b0f9e63a6d331135265869a341dce7c7f1eef158"
103
104
  }
@@ -125,6 +125,7 @@ import * as notebookCommon from '@theia/notebook/lib/common';
125
125
  import { CellExecutionUpdateType, CellRange, NotebookCellExecutionState } from '@theia/notebook/lib/common';
126
126
  import { LanguagePackBundle } from './language-pack-service';
127
127
  import { AccessibilityInformation } from '@theia/core/lib/common/accessibility';
128
+ import { TelemetryLevel } from '@theia/telemetry/lib/common/telemetry-types';
128
129
 
129
130
  import { TreeDelta } from '@theia/test/lib/common/tree-delta';
130
131
  import { TestItemDTO, TestOutputDTO, TestRunDTO, TestRunProfileDTO, TestRunRequestDTO, TestStateChangeDTO } from './test-types';
@@ -258,6 +259,12 @@ export interface PluginManagerInitializeParams {
258
259
  webview: WebviewInitData
259
260
  jsonValidation: PluginJsonValidationContribution[]
260
261
  supportedActivationEvents?: string[]
262
+ /**
263
+ * The telemetry level the plugin host starts with. Defaults to `off` when absent.
264
+ *
265
+ * @experimental
266
+ */
267
+ telemetryLevel?: TelemetryLevel
261
268
  }
262
269
 
263
270
  export interface PluginManagerStartParams {
@@ -2362,6 +2369,13 @@ export interface TelemetryMain {
2362
2369
  }
2363
2370
 
2364
2371
  export interface TelemetryExt {
2372
+ /**
2373
+ * Applies the telemetry level the user consented to. Only changes are sent this way;
2374
+ * the initial level arrives with `PluginManagerInitializeParams`.
2375
+ *
2376
+ * @experimental
2377
+ */
2378
+ $onDidChangeTelemetryLevel(level: TelemetryLevel): void;
2365
2379
  }
2366
2380
 
2367
2381
  // endregion
@@ -2524,7 +2538,7 @@ export const MAIN_RPC_CONTEXT = {
2524
2538
  THEMING_EXT: createProxyIdentifier<ThemingExt>('ThemingExt'),
2525
2539
  COMMENTS_EXT: createProxyIdentifier<CommentsExt>('CommentsExt'),
2526
2540
  TABS_EXT: createProxyIdentifier<TabsExt>('TabsExt'),
2527
- TELEMETRY_EXT: createProxyIdentifier<TelemetryExt>('TelemetryExt)'),
2541
+ TELEMETRY_EXT: createProxyIdentifier<TelemetryExt>('TelemetryExt'),
2528
2542
  TESTING_EXT: createProxyIdentifier<TestingExt>('TestingExt'),
2529
2543
  URI_EXT: createProxyIdentifier<UriExt>('UriExt'),
2530
2544
  MCP_SERVER_DEFINITION_REGISTRY_EXT: createProxyIdentifier<McpServerDefinitionRegistryExt>('McpServerDefinitionRegistryExt'),
@@ -73,6 +73,7 @@ import {
73
73
  } from '../common/hosted-plugin';
74
74
  import { isRemote } from '@theia/core/lib/browser/browser';
75
75
  import { WorkspaceTrustService } from '@theia/workspace/lib/browser/workspace-trust-service';
76
+ import { TelemetryConsentProvider } from '@theia/telemetry/lib/common/telemetry-consent-provider';
76
77
 
77
78
  export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker' | 'onDebugDynamicConfigurations';
78
79
 
@@ -183,6 +184,9 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport<PluginManag
183
184
  @inject(WorkspaceTrustService)
184
185
  protected readonly workspaceTrustService: WorkspaceTrustService;
185
186
 
187
+ @inject(TelemetryConsentProvider)
188
+ protected readonly telemetryConsentProvider: TelemetryConsentProvider;
189
+
186
190
  @inject(ILogger) @named('plugin-ext:HostedPluginSupport')
187
191
  protected override readonly logger: ILogger;
188
192
 
@@ -245,7 +249,7 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport<PluginManag
245
249
  }
246
250
 
247
251
  protected createTheiaReadyPromise(): Promise<unknown> {
248
- return Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots]);
252
+ return Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots, this.telemetryConsentProvider.ready]);
249
253
  }
250
254
 
251
255
  protected override runOperation(operation: () => Promise<void>): Promise<void> {
@@ -364,7 +368,8 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport<PluginManag
364
368
  },
365
369
  jsonValidation,
366
370
  pluginKind: isRemote ? ExtensionKind.Workspace : ExtensionKind.UI,
367
- supportedActivationEvents
371
+ supportedActivationEvents,
372
+ telemetryLevel: this.telemetryConsentProvider.level
368
373
  });
369
374
  if (toDisconnect.disposed) {
370
375
  return undefined;
@@ -34,6 +34,7 @@ import { loadManifest } from './plugin-manifest-loader';
34
34
  import { EnvExtImpl } from '../../../plugin/env';
35
35
  import { DebugExtImpl } from '../../../plugin/debug/debug-ext';
36
36
  import { LocalizationExtImpl } from '../../../plugin/localization-ext';
37
+ import { TelemetryExtImpl } from '../../../plugin/telemetry-ext';
37
38
  import pluginHostModule from './worker-plugin-module';
38
39
  import { PLUGINS_BASE_PATH } from '@theia/plugin-utils/lib/common/constants';
39
40
 
@@ -162,6 +163,7 @@ const messageRegistryExt = container.get(MessageRegistryExt);
162
163
  const clipboardExt = container.get(ClipboardExt);
163
164
  const webviewExt = container.get(WebviewsExtImpl);
164
165
  const localizationExt = container.get(LocalizationExtImpl);
166
+ const telemetryExt = container.get(TelemetryExtImpl);
165
167
  const storageProxy = container.get(KeyValueStorageProxy);
166
168
 
167
169
  const apiFactory = createAPIFactory(
@@ -175,7 +177,8 @@ const apiFactory = createAPIFactory(
175
177
  messageRegistryExt,
176
178
  clipboardExt,
177
179
  webviewExt,
178
- localizationExt
180
+ localizationExt,
181
+ telemetryExt
179
182
  );
180
183
  let defaultApi: typeof theia;
181
184
 
@@ -36,6 +36,7 @@ import { WorkerEnvExtImpl } from './worker-env-ext';
36
36
  import { DebugExtImpl } from '../../../plugin/debug/debug-ext';
37
37
  import { LocalizationExtImpl } from '../../../plugin/localization-ext';
38
38
  import { EncodingService } from '@theia/core/lib/common/encoding-service';
39
+ import { TelemetryExtImpl } from '../../../plugin/telemetry-ext';
39
40
 
40
41
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
42
  const ctx = self as any;
@@ -79,4 +80,5 @@ export default new ContainerModule(bind => {
79
80
  bind(WebviewsExtImpl).toSelf().inSingletonScope();
80
81
  bind(TerminalServiceExtImpl).toSelf().inSingletonScope();
81
82
  bind(MinimalTerminalServiceExt).toService(TerminalServiceExtImpl);
83
+ bind(TelemetryExtImpl).toSelf().inSingletonScope();
82
84
  });
@@ -39,6 +39,7 @@ import { setupPluginHostLogger } from './plugin-host-logger';
39
39
  import { LmExtImpl } from '../../plugin/lm-ext';
40
40
  import { LanguageModelToolsExtImpl } from '../../plugin/lm-tool-ext';
41
41
  import { EncodingService } from '@theia/core/lib/common/encoding-service';
42
+ import { TelemetryExtImpl } from '../../plugin/telemetry-ext';
42
43
 
43
44
  export default new ContainerModule(bind => {
44
45
  const channel = new IPCChannel();
@@ -76,4 +77,5 @@ export default new ContainerModule(bind => {
76
77
  bind(WebviewsExtImpl).toSelf().inSingletonScope();
77
78
  bind(MinimalTerminalServiceExt).toService(TerminalServiceExtImpl);
78
79
  bind(TerminalServiceExtImpl).toSelf().inSingletonScope();
80
+ bind(TelemetryExtImpl).toSelf().inSingletonScope();
79
81
  });
@@ -31,6 +31,7 @@ import {
31
31
  import { PluginMetadata, PluginModel, PluginPackage } from '../../common/plugin-protocol';
32
32
  import { createAPIFactory } from '../../plugin/plugin-context';
33
33
  import { EnvExtImpl } from '../../plugin/env';
34
+ import { TelemetryExtImpl } from '../../plugin/telemetry-ext';
34
35
  import { PreferenceRegistryExtImpl } from '../../plugin/preference-registry';
35
36
  import { ExtPluginApi, ExtPluginApiBackendInitializationFn } from '../../common/plugin-ext-api-contribution';
36
37
  import { DebugExtImpl } from '../../plugin/debug/debug-ext';
@@ -64,7 +65,8 @@ export interface ExtInterfaces {
64
65
  webviewExt: WebviewsExtImpl,
65
66
  terminalServiceExt: TerminalServiceExtImpl,
66
67
  secretsExt: SecretsExtImpl,
67
- localizationExt: LocalizationExtImpl
68
+ localizationExt: LocalizationExtImpl,
69
+ telemetryExt: TelemetryExtImpl
68
70
  }
69
71
 
70
72
  /**
@@ -352,6 +354,9 @@ export class PluginHostRPC extends AbstractPluginHostRPC<PluginManagerExtImpl, P
352
354
  @inject(SecretsExtImpl)
353
355
  protected readonly secretsExt: SecretsExtImpl;
354
356
 
357
+ @inject(TelemetryExtImpl)
358
+ protected readonly telemetryExt: TelemetryExtImpl;
359
+
355
360
  constructor() {
356
361
  super('PLUGIN_HOST', '/scanners/backend-init-theia.js',
357
362
  {
@@ -380,18 +385,19 @@ export class PluginHostRPC extends AbstractPluginHostRPC<PluginManagerExtImpl, P
380
385
  webviewExt: this.webviewExt,
381
386
  terminalServiceExt: this.terminalServiceExt,
382
387
  secretsExt: this.secretsExt,
383
- localizationExt: this.localizationExt
388
+ localizationExt: this.localizationExt,
389
+ telemetryExt: this.telemetryExt
384
390
  };
385
391
  }
386
392
 
387
393
  protected createAPIFactory(extInterfaces: ExtInterfaces): PluginAPIFactory {
388
394
  const {
389
395
  envExt, debugExt, preferenceRegistryExt, editorsAndDocumentsExt, workspaceExt,
390
- messageRegistryExt, clipboardExt, webviewExt, localizationExt
396
+ messageRegistryExt, clipboardExt, webviewExt, localizationExt, telemetryExt
391
397
  } = extInterfaces;
392
398
  return createAPIFactory(this.rpc, this.pluginManager, envExt, debugExt, preferenceRegistryExt,
393
399
  editorsAndDocumentsExt, workspaceExt, messageRegistryExt, clipboardExt, webviewExt,
394
- localizationExt);
400
+ localizationExt, telemetryExt);
395
401
  }
396
402
 
397
403
  protected initExtApi(extApi: ExtPluginApi): void {
@@ -0,0 +1,140 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 JuliaHub, Inc. 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 { Emitter, URI } from '@theia/core';
22
+ import { BaseWidget, Navigatable, Saveable, SaveableSource, Widget } from '@theia/core/lib/browser';
23
+ import { SaveableService } from '@theia/core/lib/browser/saveable-service';
24
+ import { EditorManager, EditorWidget } from '@theia/editor/lib/browser';
25
+ import { EditorsAndDocumentsMain } from './editors-and-documents-main';
26
+
27
+ disableJSDOM();
28
+
29
+ class TestSaveable implements Saveable {
30
+ dirty = true;
31
+ readonly onDirtyChanged = new Emitter<void>().event;
32
+ readonly onContentChanged = new Emitter<void>().event;
33
+ saveCount = 0;
34
+
35
+ async save(): Promise<void> {
36
+ this.saveCount++;
37
+ this.dirty = false;
38
+ }
39
+ }
40
+
41
+ /** Navigatable to the resource, but with nothing to save. */
42
+ class TestNavigatableWidget extends BaseWidget implements Navigatable {
43
+ constructor(private readonly uri: URI) {
44
+ super();
45
+ }
46
+
47
+ getResourceUri(): URI {
48
+ return this.uri;
49
+ }
50
+
51
+ createMoveToUri(): URI {
52
+ return this.uri;
53
+ }
54
+ }
55
+
56
+ /** Stands in for a `CustomEditorWidget`: saveable and navigatable, but not an `EditorWidget`. */
57
+ class TestSaveableWidget extends TestNavigatableWidget implements SaveableSource {
58
+ readonly saveable = new TestSaveable();
59
+ }
60
+
61
+ function createEditorsAndDocumentsMain(
62
+ editor: EditorWidget | undefined,
63
+ widgets: Widget[]
64
+ ): EditorsAndDocumentsMain {
65
+ // Bypass the constructor's RPC/container wiring: `save` and `saveAs` only touch the
66
+ // editor manager, the shell and the saveable service.
67
+ const main = Object.create(EditorsAndDocumentsMain.prototype) as EditorsAndDocumentsMain;
68
+ const fields = main as unknown as Record<string, unknown>;
69
+ fields.editorManager = { getByUri: async () => editor } as unknown as EditorManager;
70
+ fields.shell = { widgets };
71
+ fields.saveResourceService = new SaveableService();
72
+ return main;
73
+ }
74
+
75
+ describe('EditorsAndDocumentsMain#save', () => {
76
+
77
+ before(() => {
78
+ disableJSDOM = enableJSDOM();
79
+ });
80
+
81
+ after(() => {
82
+ disableJSDOM();
83
+ });
84
+
85
+ const uri = new URI('custom-editor:/some/resource?viewType=test');
86
+
87
+ it('saves a saveable widget that the editor manager does not know', async () => {
88
+ const widget = new TestSaveableWidget(uri);
89
+ const main = createEditorsAndDocumentsMain(undefined, [widget]);
90
+
91
+ const saved = await main.save(uri);
92
+
93
+ expect(widget.saveable.saveCount).to.equal(1);
94
+ expect(saved?.toString()).to.equal(uri.toString());
95
+ });
96
+
97
+ it('prefers the text editor when the editor manager has one', async () => {
98
+ const editor = new TestSaveableWidget(uri);
99
+ const custom = new TestSaveableWidget(uri);
100
+ const main = createEditorsAndDocumentsMain(editor as unknown as EditorWidget, [custom]);
101
+
102
+ await main.save(uri);
103
+
104
+ expect(editor.saveable.saveCount).to.equal(1);
105
+ expect(custom.saveable.saveCount).to.equal(0);
106
+ });
107
+
108
+ it('leaves widgets bound to another resource alone', async () => {
109
+ const other = new TestSaveableWidget(new URI('custom-editor:/other/resource'));
110
+ const main = createEditorsAndDocumentsMain(undefined, [other]);
111
+
112
+ const saved = await main.save(uri);
113
+
114
+ expect(other.saveable.saveCount).to.equal(0);
115
+ expect(saved).to.be.undefined;
116
+ });
117
+
118
+ it('ignores a widget that is navigatable to the resource but not saveable', async () => {
119
+ const main = createEditorsAndDocumentsMain(undefined, [new TestNavigatableWidget(uri)]);
120
+
121
+ expect(await main.save(uri)).to.be.undefined;
122
+ });
123
+
124
+ it('resolves saveAs through the same fallback', async () => {
125
+ const widget = new TestSaveableWidget(uri);
126
+ const main = createEditorsAndDocumentsMain(undefined, [widget]);
127
+ const saveAsTargets: Widget[] = [];
128
+ // `SaveableService.canSaveAs` is unconditionally false on the base class, so the
129
+ // service is stubbed to reach the `saveAs` branch at all.
130
+ (main as unknown as Record<string, unknown>).saveResourceService = {
131
+ canSaveAs: (candidate: Widget) => candidate === widget,
132
+ saveAs: async (target: Widget) => { saveAsTargets.push(target); return uri; }
133
+ };
134
+
135
+ const saved = await main.saveAs(uri);
136
+
137
+ expect(saveAsTargets).to.deep.equal([widget]);
138
+ expect(saved?.toString()).to.equal(uri.toString());
139
+ });
140
+ });
@@ -33,6 +33,7 @@ import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
33
33
  import { TextEditorMain } from './text-editor-main';
34
34
  import { DisposableCollection, Emitter, URI } from '@theia/core';
35
35
  import { EditorManager, EditorWidget } from '@theia/editor/lib/browser';
36
+ import { ApplicationShell, NavigatableWidget, Saveable, Widget } from '@theia/core/lib/browser';
36
37
  import { SaveableService } from '@theia/core/lib/browser/saveable-service';
37
38
  import { TabsMainImpl } from './tabs/tabs-main';
38
39
  import { NotebookCellEditorService, NotebookEditorWidgetService } from '@theia/notebook/lib/browser';
@@ -48,6 +49,7 @@ export class EditorsAndDocumentsMain implements Disposable {
48
49
 
49
50
  private readonly modelService: EditorModelService;
50
51
  private readonly editorManager: EditorManager;
52
+ private readonly shell: ApplicationShell;
51
53
  private readonly saveResourceService: SaveableService;
52
54
  private readonly encodingRegistry: EncodingRegistry;
53
55
 
@@ -69,6 +71,7 @@ export class EditorsAndDocumentsMain implements Disposable {
69
71
  this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT);
70
72
 
71
73
  this.editorManager = container.get(EditorManager);
74
+ this.shell = container.get(ApplicationShell);
72
75
  this.modelService = container.get(EditorModelService);
73
76
  this.saveResourceService = container.get(SaveableService);
74
77
  this.encodingRegistry = container.get(EncodingRegistry);
@@ -183,22 +186,31 @@ export class EditorsAndDocumentsMain implements Disposable {
183
186
  }
184
187
 
185
188
  async save(uri: URI): Promise<URI | undefined> {
186
- const editor = await this.editorManager.getByUri(uri);
187
- if (!editor) {
189
+ const widget = await this.getSaveTarget(uri);
190
+ if (!widget) {
188
191
  return undefined;
189
192
  }
190
- return this.saveResourceService.save(editor);
193
+ return this.saveResourceService.save(widget);
191
194
  }
192
195
 
193
196
  async saveAs(uri: URI): Promise<URI | undefined> {
194
- const editor = await this.editorManager.getByUri(uri);
195
- if (!editor) {
197
+ const widget = await this.getSaveTarget(uri);
198
+ if (!widget) {
196
199
  return undefined;
197
200
  }
198
- if (!this.saveResourceService.canSaveAs(editor)) {
201
+ if (!this.saveResourceService.canSaveAs(widget)) {
199
202
  return undefined;
200
203
  }
201
- return this.saveResourceService.saveAs(editor);
204
+ return this.saveResourceService.saveAs(widget);
205
+ }
206
+
207
+ /**
208
+ * Resolve the widget holding `uri`, preferring a text editor. Custom editors and notebooks
209
+ * are not opened by the `EditorManager`, so they are only reachable through the shell.
210
+ */
211
+ protected async getSaveTarget(uri: URI): Promise<Widget | undefined> {
212
+ return await this.editorManager.getByUri(uri)
213
+ ?? this.shell.widgets.find(widget => Saveable.get(widget) && NavigatableWidget.getUri(widget)?.isEqual(uri));
202
214
  }
203
215
 
204
216
  saveAll(includeUntitled?: boolean): Promise<boolean> {