@theia/core 1.58.3 → 1.59.0-next.72

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 (87) hide show
  1. package/README.md +6 -6
  2. package/i18n/nls.json +1 -1
  3. package/lib/browser/catalog.json +26 -7
  4. package/lib/browser/logger-frontend-module.d.ts.map +1 -1
  5. package/lib/browser/logger-frontend-module.js +2 -3
  6. package/lib/browser/logger-frontend-module.js.map +1 -1
  7. package/lib/browser/menu/browser-menu-plugin.d.ts +7 -0
  8. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  9. package/lib/browser/menu/browser-menu-plugin.js +46 -3
  10. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  11. package/lib/browser/navigatable-types.d.ts.map +1 -1
  12. package/lib/browser/navigatable-types.js +1 -1
  13. package/lib/browser/navigatable-types.js.map +1 -1
  14. package/lib/browser/saveable-service.js +1 -1
  15. package/lib/browser/saveable-service.js.map +1 -1
  16. package/lib/browser/saveable.d.ts +2 -0
  17. package/lib/browser/saveable.d.ts.map +1 -1
  18. package/lib/browser/saveable.js.map +1 -1
  19. package/lib/common/array-utils.d.ts +2 -0
  20. package/lib/common/array-utils.d.ts.map +1 -1
  21. package/lib/common/array-utils.js +24 -0
  22. package/lib/common/array-utils.js.map +1 -1
  23. package/lib/common/content-replacer.d.ts +56 -0
  24. package/lib/common/content-replacer.d.ts.map +1 -0
  25. package/lib/common/content-replacer.js +139 -0
  26. package/lib/common/content-replacer.js.map +1 -0
  27. package/lib/common/content-replacer.spec.d.ts +2 -0
  28. package/lib/common/content-replacer.spec.d.ts.map +1 -0
  29. package/lib/common/content-replacer.spec.js +115 -0
  30. package/lib/common/content-replacer.spec.js.map +1 -0
  31. package/lib/common/logger-binding.d.ts +3 -0
  32. package/lib/common/logger-binding.d.ts.map +1 -0
  33. package/lib/common/logger-binding.js +36 -0
  34. package/lib/common/logger-binding.js.map +1 -0
  35. package/lib/common/logger-protocol.d.ts +1 -1
  36. package/lib/common/logger-protocol.d.ts.map +1 -1
  37. package/lib/common/logger-protocol.js +3 -1
  38. package/lib/common/logger-protocol.js.map +1 -1
  39. package/lib/common/logger.d.ts +1 -0
  40. package/lib/common/logger.d.ts.map +1 -1
  41. package/lib/common/logger.js +12 -1
  42. package/lib/common/logger.js.map +1 -1
  43. package/lib/common/menu/composite-menu-node.d.ts +2 -2
  44. package/lib/common/menu/composite-menu-node.d.ts.map +1 -1
  45. package/lib/common/menu/composite-menu-node.js +2 -0
  46. package/lib/common/menu/composite-menu-node.js.map +1 -1
  47. package/lib/common/menu/menu-model-registry.d.ts +22 -4
  48. package/lib/common/menu/menu-model-registry.d.ts.map +1 -1
  49. package/lib/common/menu/menu-model-registry.js +74 -17
  50. package/lib/common/menu/menu-model-registry.js.map +1 -1
  51. package/lib/common/menu/menu-types.d.ts +2 -1
  52. package/lib/common/menu/menu-types.d.ts.map +1 -1
  53. package/lib/common/menu/menu-types.js.map +1 -1
  54. package/lib/common/resource.d.ts +11 -2
  55. package/lib/common/resource.d.ts.map +1 -1
  56. package/lib/common/resource.js +11 -1
  57. package/lib/common/resource.js.map +1 -1
  58. package/lib/node/console-logger-server.d.ts +5 -1
  59. package/lib/node/console-logger-server.d.ts.map +1 -1
  60. package/lib/node/console-logger-server.js +18 -4
  61. package/lib/node/console-logger-server.js.map +1 -1
  62. package/lib/node/logger-backend-module.d.ts.map +1 -1
  63. package/lib/node/logger-backend-module.js +2 -3
  64. package/lib/node/logger-backend-module.js.map +1 -1
  65. package/lib/node/logger-cli-contribution.d.ts +2 -0
  66. package/lib/node/logger-cli-contribution.d.ts.map +1 -1
  67. package/lib/node/logger-cli-contribution.js +40 -7
  68. package/lib/node/logger-cli-contribution.js.map +1 -1
  69. package/package.json +6 -6
  70. package/src/browser/logger-frontend-module.ts +3 -4
  71. package/src/browser/menu/browser-menu-plugin.ts +55 -5
  72. package/src/browser/navigatable-types.ts +1 -1
  73. package/src/browser/saveable-service.ts +1 -1
  74. package/src/browser/saveable.ts +2 -0
  75. package/src/common/array-utils.ts +25 -0
  76. package/src/common/content-replacer.spec.ts +124 -0
  77. package/src/common/content-replacer.ts +151 -0
  78. package/src/common/logger-binding.ts +34 -0
  79. package/src/common/logger-protocol.ts +4 -2
  80. package/src/common/logger.ts +10 -1
  81. package/src/common/menu/composite-menu-node.ts +4 -2
  82. package/src/common/menu/menu-model-registry.ts +84 -19
  83. package/src/common/menu/menu-types.ts +2 -1
  84. package/src/common/resource.ts +17 -4
  85. package/src/node/console-logger-server.ts +22 -2
  86. package/src/node/logger-backend-module.ts +3 -4
  87. package/src/node/logger-cli-contribution.ts +43 -7
@@ -1 +1 @@
1
- {"version":3,"file":"logger-cli-contribution.d.ts","sourceRoot":"","sources":["../../src/node/logger-cli-contribution.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGxC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGjD,wCAAwC;AACxC,MAAM,WAAW,SAAS;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC3B;AAED;;;;GAIG;AACH,qBACa,uBAAwB,YAAW,eAAe;IAE3D,SAAS,CAAC,UAAU,EAAE,SAAS,CAAM;IAErC;;OAEG;IACH,SAAS,CAAC,gBAAgB,EAAE,QAAQ,CAAiB;IAErD,SAAS,CAAC,qBAAqB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAuB;IAErE,IAAI,eAAe,IAAI,QAAQ,CAE9B;IAED,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI;IAc3B,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;cAsBxC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAsBnD,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BnE,IAAI,kBAAkB,IAAI,KAAK,CAAC,IAAI,CAAC,CAEpC;IAED,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAUzC;;OAEG;IACH,SAAS,CAAC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,QAAQ;CASrF"}
1
+ {"version":3,"file":"logger-cli-contribution.d.ts","sourceRoot":"","sources":["../../src/node/logger-cli-contribution.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGxC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGjD,wCAAwC;AACxC,MAAM,WAAW,SAAS;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC3B;AAED;;;;GAIG;AACH,qBACa,uBAAwB,YAAW,eAAe;IAE3D,SAAS,CAAC,UAAU,EAAE,SAAS,CAAM;IAErC;;OAEG;IACH,SAAS,CAAC,gBAAgB,EAAE,QAAQ,CAAiB;IAErD,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE5B,SAAS,CAAC,qBAAqB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAuB;IAErE,IAAI,eAAe,IAAI,QAAQ,CAE9B;IAED,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;IAED,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI;IAoB3B,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;cA2CxC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAyBnD,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BnE,IAAI,kBAAkB,IAAI,KAAK,CAAC,IAAI,CAAC,CAEpC;IAED,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAUzC;;OAEG;IACH,SAAS,CAAC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,QAAQ;CASrF"}
@@ -43,6 +43,9 @@ let LogLevelCliContribution = class LogLevelCliContribution {
43
43
  get logLevels() {
44
44
  return this._logLevels;
45
45
  }
46
+ get logFile() {
47
+ return this._logFile;
48
+ }
46
49
  configure(conf) {
47
50
  conf.option('log-level', {
48
51
  description: 'Sets the default log level',
@@ -54,6 +57,11 @@ let LogLevelCliContribution = class LogLevelCliContribution {
54
57
  type: 'string',
55
58
  nargs: 1,
56
59
  });
60
+ conf.option('log-file', {
61
+ description: 'Path to the log file',
62
+ type: 'string',
63
+ nargs: 1
64
+ });
57
65
  }
58
66
  async setArguments(args) {
59
67
  if (args['log-level'] !== undefined && args['log-config'] !== undefined) {
@@ -73,21 +81,46 @@ let LogLevelCliContribution = class LogLevelCliContribution {
73
81
  console.error(`Error reading log config file ${filename}: ${e}`);
74
82
  }
75
83
  }
84
+ if (args['log-file'] !== undefined) {
85
+ let filename = args['log-file'];
86
+ try {
87
+ filename = path.resolve(filename);
88
+ try {
89
+ const stat = await fs.stat(filename);
90
+ if (stat && stat.isFile()) {
91
+ // Rename the previous log file to avoid overwriting it
92
+ const oldFilename = `${filename}.${stat.ctime.toISOString().replace(/:/g, '-')}.old`;
93
+ await fs.rename(filename, oldFilename);
94
+ }
95
+ }
96
+ catch {
97
+ // File does not exist, just continue to create it
98
+ }
99
+ await fs.writeFile(filename, '');
100
+ this._logFile = filename;
101
+ }
102
+ catch (e) {
103
+ console.error(`Error creating log file ${filename}: ${e}`);
104
+ }
105
+ }
76
106
  }
77
107
  async watchLogConfigFile(filename) {
78
- await (0, watcher_1.subscribe)(filename, async (err, events) => {
108
+ const dir = path.dirname(filename);
109
+ await (0, watcher_1.subscribe)(dir, async (err, events) => {
79
110
  if (err) {
80
111
  console.log(`Error during log file watching ${filename}: ${err}`);
81
112
  return;
82
113
  }
83
114
  try {
84
115
  for (const event of events) {
85
- switch (event.type) {
86
- case 'create':
87
- case 'update':
88
- await this.slurpLogConfigFile(filename);
89
- this.logConfigChangedEvent.fire(undefined);
90
- break;
116
+ if (event.path === filename) {
117
+ switch (event.type) {
118
+ case 'create':
119
+ case 'update':
120
+ await this.slurpLogConfigFile(filename);
121
+ this.logConfigChangedEvent.fire(undefined);
122
+ break;
123
+ }
91
124
  }
92
125
  }
93
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"logger-cli-contribution.js","sourceRoot":"","sources":["../../src/node/logger-cli-contribution.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger-cli-contribution.js","sourceRoot":"","sources":["../../src/node/logger-cli-contribution.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theia/core",
3
- "version": "1.58.3",
3
+ "version": "1.59.0-next.72+f41d8efcd",
4
4
  "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.",
5
5
  "main": "lib/common/index.js",
6
6
  "typings": "lib/common/index.d.ts",
@@ -17,8 +17,8 @@
17
17
  "@phosphor/signaling": "1",
18
18
  "@phosphor/virtualdom": "1",
19
19
  "@phosphor/widgets": "1",
20
- "@theia/application-package": "1.58.3",
21
- "@theia/request": "1.58.3",
20
+ "@theia/application-package": "1.59.0-next.72+f41d8efcd",
21
+ "@theia/request": "1.59.0-next.72+f41d8efcd",
22
22
  "@types/body-parser": "^1.16.4",
23
23
  "@types/cookie": "^0.3.3",
24
24
  "@types/dompurify": "^2.2.2",
@@ -210,13 +210,13 @@
210
210
  "watch": "theiaext watch"
211
211
  },
212
212
  "devDependencies": {
213
- "@theia/ext-scripts": "1.58.3",
214
- "@theia/re-exports": "1.58.3",
213
+ "@theia/ext-scripts": "1.58.0",
214
+ "@theia/re-exports": "1.58.0",
215
215
  "minimist": "^1.2.0",
216
216
  "nodejs-file-downloader": "4.13.0"
217
217
  },
218
218
  "nyc": {
219
219
  "extends": "../../configs/nyc.json"
220
220
  },
221
- "gitHead": "ca70c15332f91e0f61b12cd147b5ff8326e2e6d2"
221
+ "gitHead": "f41d8efcd4abb79167b74bf476eafc7857e97306"
222
222
  }
@@ -16,11 +16,12 @@
16
16
 
17
17
  import { ContainerModule, Container } from 'inversify';
18
18
  import { ILoggerServer, loggerPath, ConsoleLogger } from '../common/logger-protocol';
19
- import { ILogger, Logger, LoggerFactory, setRootLogger, LoggerName, rootLoggerName } from '../common/logger';
19
+ import { ILogger, Logger, LoggerFactory, setRootLogger, LoggerName } from '../common/logger';
20
20
  import { LoggerWatcher } from '../common/logger-watcher';
21
21
  import { WebSocketConnectionProvider } from './messaging';
22
22
  import { FrontendApplicationContribution } from './frontend-application-contribution';
23
23
  import { EncodingError } from '../common/message-rpc/rpc-message-encoder';
24
+ import { bindCommonLogger } from '../common/logger-binding';
24
25
 
25
26
  export const loggerFrontendModule = new ContainerModule(bind => {
26
27
  bind(FrontendApplicationContribution).toDynamicValue(ctx => ({
@@ -29,9 +30,7 @@ export const loggerFrontendModule = new ContainerModule(bind => {
29
30
  }
30
31
  }));
31
32
 
32
- bind(LoggerName).toConstantValue(rootLoggerName);
33
- bind(ILogger).to(Logger).inSingletonScope().whenTargetIsDefault();
34
- bind(LoggerWatcher).toSelf().inSingletonScope();
33
+ bindCommonLogger(bind);
35
34
  bind(ILoggerServer).toDynamicValue(ctx => {
36
35
  const loggerWatcher = ctx.container.get(LoggerWatcher);
37
36
  const connection = ctx.container.get(WebSocketConnectionProvider);
@@ -15,18 +15,20 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { injectable, inject } from 'inversify';
18
- import { MenuBar, Menu as MenuWidget, Widget } from '@phosphor/widgets';
18
+ import { Menu, MenuBar, Menu as MenuWidget, Widget } from '@phosphor/widgets';
19
19
  import { CommandRegistry as PhosphorCommandRegistry } from '@phosphor/commands';
20
+ import { ElementExt } from '@phosphor/domutils';
20
21
  import {
21
22
  CommandRegistry, environment, DisposableCollection, Disposable,
22
- MenuModelRegistry, MAIN_MENU_BAR, MenuPath, MenuNode, MenuCommandExecutor, CompoundMenuNode, CompoundMenuNodeRole, CommandMenuNode
23
+ MenuModelRegistry, MAIN_MENU_BAR, MenuPath, MenuNode, MenuCommandExecutor, CompoundMenuNode, CompoundMenuNodeRole, CommandMenuNode,
24
+ ArrayUtils
23
25
  } from '../../common';
24
26
  import { KeybindingRegistry } from '../keybinding';
25
27
  import { FrontendApplication } from '../frontend-application';
26
28
  import { FrontendApplicationContribution } from '../frontend-application-contribution';
27
29
  import { ContextKeyService, ContextMatcher } from '../context-key-service';
28
30
  import { ContextMenuContext } from './context-menu-context';
29
- import { waitForRevealed } from '../widgets';
31
+ import { Message, waitForRevealed } from '../widgets';
30
32
  import { ApplicationShell } from '../shell';
31
33
  import { CorePreferences } from '../core-preferences';
32
34
  import { PreferenceService } from '../preferences/preference-service';
@@ -82,8 +84,10 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
82
84
  this.keybindingRegistry.onKeybindingsChanged(() => {
83
85
  this.showMenuBar(menuBar);
84
86
  }),
85
- this.menuProvider.onDidChange(() => {
86
- this.showMenuBar(menuBar);
87
+ this.menuProvider.onDidChange(evt => {
88
+ if (ArrayUtils.startsWith(evt.path, MAIN_MENU_BAR)) {
89
+ this.showMenuBar(menuBar);
90
+ }
87
91
  })
88
92
  );
89
93
  menuBar.disposed.connect(() => disposable.dispose());
@@ -156,6 +160,10 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
156
160
 
157
161
  }
158
162
 
163
+ export function isMenuElement(element: HTMLElement | null): boolean {
164
+ return !!element && element.className.includes('p-Menu');
165
+ }
166
+
159
167
  export class DynamicMenuBarWidget extends MenuBarWidget {
160
168
 
161
169
  /**
@@ -263,6 +271,48 @@ export class DynamicMenuWidget extends MenuWidget {
263
271
  this.updateSubMenus(this, this.menu, this.options.commands);
264
272
  }
265
273
 
274
+ protected override onAfterAttach(msg: Message): void {
275
+ super.onAfterAttach(msg);
276
+ this.node.ownerDocument.addEventListener('pointerdown', this, true);
277
+ }
278
+
279
+ protected override onBeforeDetach(msg: Message): void {
280
+ this.node.ownerDocument.removeEventListener('pointerdown', this);
281
+ super.onAfterDetach(msg);
282
+ }
283
+
284
+ override handleEvent(event: Event): void {
285
+ if (event.type === 'pointerdown') {
286
+ this.handlePointerDown(event as PointerEvent);
287
+ }
288
+ super.handleEvent(event);
289
+ }
290
+
291
+ handlePointerDown(event: PointerEvent): void {
292
+ // this code is copied from the superclass because we cannot use the hit
293
+ // test from the "Private" implementation namespace
294
+ if (this['_parentMenu']) {
295
+ return;
296
+ }
297
+
298
+ // The mouse button which is pressed is irrelevant. If the press
299
+ // is not on a menu, the entire hierarchy is closed and the event
300
+ // is allowed to propagate. This allows other code to act on the
301
+ // event, such as focusing the clicked element.
302
+ if (!this.hitTestMenus(this, event.clientX, event.clientY)) {
303
+ this.close();
304
+ }
305
+ }
306
+
307
+ private hitTestMenus(menu: Menu, x: number, y: number): boolean {
308
+ for (let temp: Menu | null = menu; temp; temp = temp.childMenu) {
309
+ if (ElementExt.hitTest(temp.node, x, y)) {
310
+ return true;
311
+ }
312
+ }
313
+ return false;
314
+ }
315
+
266
316
  public aboutToShow({ previousFocusedElement }: { previousFocusedElement: HTMLElement | undefined }): void {
267
317
  this.preserveFocusedElement(previousFocusedElement);
268
318
  this.clearItems();
@@ -42,7 +42,7 @@ export namespace NavigatableWidget {
42
42
  export function is(arg: unknown): arg is NavigatableWidget {
43
43
  return arg instanceof BaseWidget && Navigatable.is(arg);
44
44
  }
45
- export function* getAffected<T extends Widget>(
45
+ export function getAffected<T extends Widget>(
46
46
  widgets: Iterable<T>,
47
47
  context: MaybeArray<URI>
48
48
  ): IterableIterator<[URI, T & NavigatableWidget]> {
@@ -162,7 +162,7 @@ export class SaveableService implements FrontendApplicationContribution {
162
162
  // Never auto-save untitled documents
163
163
  return false;
164
164
  } else {
165
- return saveable.dirty;
165
+ return saveable.autosaveable !== false && saveable.dirty;
166
166
  }
167
167
  }
168
168
 
@@ -28,6 +28,8 @@ export type AutoSaveMode = 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowCha
28
28
 
29
29
  export interface Saveable {
30
30
  readonly dirty: boolean;
31
+ /** If false, the saveable will not participate in autosaving. */
32
+ readonly autosaveable?: boolean;
31
33
  /**
32
34
  * This event is fired when the content of the `dirty` variable changes.
33
35
  */
@@ -126,4 +126,29 @@ export namespace ArrayUtils {
126
126
  }
127
127
  return result;
128
128
  }
129
+
130
+ export function shallowEqual<T>(left: readonly T[], right: readonly T[]): boolean {
131
+ if (left.length !== right.length) {
132
+ return false;
133
+ }
134
+ for (let i = 0; i < left.length; i++) {
135
+ if (left[i] !== right[i]) {
136
+ return false;
137
+ }
138
+ }
139
+ return true;
140
+ }
141
+
142
+ export function startsWith<T>(left: readonly T[], right: readonly T[]): boolean {
143
+ if (right.length > left.length) {
144
+ return false;
145
+ }
146
+
147
+ for (let i = 0; i < right.length; i++) {
148
+ if (left[i] !== right[i]) {
149
+ return false;
150
+ }
151
+ }
152
+ return true;
153
+ }
129
154
  }
@@ -0,0 +1,124 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
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 { expect } from 'chai';
18
+ import { ContentReplacer, Replacement } from './content-replacer';
19
+
20
+ describe('ContentReplacer', () => {
21
+ let contentReplacer: ContentReplacer;
22
+
23
+ before(() => {
24
+ contentReplacer = new ContentReplacer();
25
+ });
26
+
27
+ it('should replace content when oldContent matches exactly', () => {
28
+ const originalContent = 'Hello World!';
29
+ const replacements: Replacement[] = [
30
+ { oldContent: 'World', newContent: 'Universe' }
31
+ ];
32
+ const expectedContent = 'Hello Universe!';
33
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
34
+ expect(result.updatedContent).to.equal(expectedContent);
35
+ expect(result.errors).to.be.empty;
36
+ });
37
+
38
+ it('should replace content when oldContent matches after trimming lines', () => {
39
+ const originalContent = 'Line one\n Line two \nLine three';
40
+ const replacements: Replacement[] = [
41
+ { oldContent: 'Line two', newContent: 'Second Line' }
42
+ ];
43
+ const expectedContent = 'Line one\n Second Line \nLine three';
44
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
45
+ expect(result.updatedContent).to.equal(expectedContent);
46
+ expect(result.errors).to.be.empty;
47
+ });
48
+
49
+ it('should return an error when oldContent is not found', () => {
50
+ const originalContent = 'Sample content';
51
+ const replacements: Replacement[] = [
52
+ { oldContent: 'Nonexistent', newContent: 'Replacement' }
53
+ ];
54
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
55
+ expect(result.updatedContent).to.equal(originalContent);
56
+ expect(result.errors).to.include('Content to replace not found: "Nonexistent"');
57
+ });
58
+
59
+ it('should return an error when oldContent has multiple occurrences', () => {
60
+ const originalContent = 'Repeat Repeat Repeat';
61
+ const replacements: Replacement[] = [
62
+ { oldContent: 'Repeat', newContent: 'Once' }
63
+ ];
64
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
65
+ expect(result.updatedContent).to.equal(originalContent);
66
+ expect(result.errors).to.include('Multiple occurrences found for: "Repeat"');
67
+ });
68
+
69
+ it('should prepend newContent when oldContent is an empty string', () => {
70
+ const originalContent = 'Existing content';
71
+ const replacements: Replacement[] = [
72
+ { oldContent: '', newContent: 'Prepended content\n' }
73
+ ];
74
+ const expectedContent = 'Prepended content\nExisting content';
75
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
76
+ expect(result.updatedContent).to.equal(expectedContent);
77
+ expect(result.errors).to.be.empty;
78
+ });
79
+
80
+ it('should handle multiple replacements correctly', () => {
81
+ const originalContent = 'Foo Bar Baz';
82
+ const replacements: Replacement[] = [
83
+ { oldContent: 'Foo', newContent: 'FooModified' },
84
+ { oldContent: 'Bar', newContent: 'BarModified' },
85
+ { oldContent: 'Baz', newContent: 'BazModified' }
86
+ ];
87
+ const expectedContent = 'FooModified BarModified BazModified';
88
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
89
+ expect(result.updatedContent).to.equal(expectedContent);
90
+ expect(result.errors).to.be.empty;
91
+ });
92
+
93
+ it('should replace all occurrences when mutiple is true', () => {
94
+ const originalContent = 'Repeat Repeat Repeat';
95
+ const replacements: Replacement[] = [
96
+ { oldContent: 'Repeat', newContent: 'Once', multiple: true }
97
+ ];
98
+ const expectedContent = 'Once Once Once';
99
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
100
+ expect(result.updatedContent).to.equal(expectedContent);
101
+ expect(result.errors).to.be.empty;
102
+ });
103
+
104
+ it('should return an error when mutiple is false and multiple occurrences are found', () => {
105
+ const originalContent = 'Repeat Repeat Repeat';
106
+ const replacements: Replacement[] = [
107
+ { oldContent: 'Repeat', newContent: 'Once', multiple: false }
108
+ ];
109
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
110
+ expect(result.updatedContent).to.equal(originalContent);
111
+ expect(result.errors).to.include('Multiple occurrences found for: "Repeat"');
112
+ });
113
+
114
+ it('should return an error when conflicting replacements for the same oldContent are provided', () => {
115
+ const originalContent = 'Conflict test content';
116
+ const replacements: Replacement[] = [
117
+ { oldContent: 'test', newContent: 'test1' },
118
+ { oldContent: 'test', newContent: 'test2' }
119
+ ];
120
+ const result = contentReplacer.applyReplacements(originalContent, replacements);
121
+ expect(result.updatedContent).to.equal(originalContent);
122
+ expect(result.errors).to.include('Conflicting replacement values for: "test"');
123
+ });
124
+ });
@@ -0,0 +1,151 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
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
+ export interface Replacement {
18
+ oldContent: string;
19
+ newContent: string;
20
+ multiple?: boolean;
21
+ }
22
+
23
+ export class ContentReplacer {
24
+ /**
25
+ * Applies a list of replacements to the original content using a multi-step matching strategy.
26
+ * @param originalContent The original file content.
27
+ * @param replacements Array of Replacement objects.
28
+ * @param allowMultiple If true, all occurrences of each oldContent will be replaced. If false, an error is returned when multiple occurrences are found.
29
+ * @returns An object containing the updated content and any error messages.
30
+ */
31
+ applyReplacements(originalContent: string, replacements: Replacement[]): { updatedContent: string, errors: string[] } {
32
+ let updatedContent = originalContent;
33
+ const errorMessages: string[] = [];
34
+
35
+ // Guard against conflicting replacements: if the same oldContent appears with different newContent, return with an error.
36
+ const conflictMap = new Map<string, string>();
37
+ for (const replacement of replacements) {
38
+ if (conflictMap.has(replacement.oldContent) && conflictMap.get(replacement.oldContent) !== replacement.newContent) {
39
+ return { updatedContent: originalContent, errors: [`Conflicting replacement values for: "${replacement.oldContent}"`] };
40
+ }
41
+ conflictMap.set(replacement.oldContent, replacement.newContent);
42
+ }
43
+
44
+ replacements.forEach(({ oldContent, newContent, multiple }) => {
45
+ // If the old content is empty, prepend the new content to the beginning of the file (e.g. in new file)
46
+ if (oldContent === '') {
47
+ updatedContent = newContent + updatedContent;
48
+ return;
49
+ }
50
+
51
+ let matchIndices = this.findExactMatches(updatedContent, oldContent);
52
+
53
+ if (matchIndices.length === 0) {
54
+ matchIndices = this.findLineTrimmedMatches(updatedContent, oldContent);
55
+ }
56
+
57
+ if (matchIndices.length === 0) {
58
+ errorMessages.push(`Content to replace not found: "${oldContent}"`);
59
+ } else if (matchIndices.length > 1) {
60
+ if (multiple) {
61
+ updatedContent = this.replaceContentAll(updatedContent, oldContent, newContent);
62
+ } else {
63
+ errorMessages.push(`Multiple occurrences found for: "${oldContent}"`);
64
+ }
65
+ } else {
66
+ updatedContent = this.replaceContentOnce(updatedContent, oldContent, newContent);
67
+ }
68
+ });
69
+
70
+ return { updatedContent, errors: errorMessages };
71
+ }
72
+
73
+ /**
74
+ * Finds all exact matches of a substring within a string.
75
+ * @param content The content to search within.
76
+ * @param search The substring to search for.
77
+ * @returns An array of starting indices where the exact substring is found.
78
+ */
79
+ private findExactMatches(content: string, search: string): number[] {
80
+ const indices: number[] = [];
81
+ let startIndex = 0;
82
+
83
+ while ((startIndex = content.indexOf(search, startIndex)) !== -1) {
84
+ indices.push(startIndex);
85
+ startIndex += search.length;
86
+ }
87
+
88
+ return indices;
89
+ }
90
+
91
+ /**
92
+ * Attempts to find matches by trimming whitespace from lines in the original content and the search string.
93
+ * @param content The original content.
94
+ * @param search The substring to search for, potentially with varying whitespace.
95
+ * @returns An array of starting indices where a trimmed match is found.
96
+ */
97
+ private findLineTrimmedMatches(content: string, search: string): number[] {
98
+ const trimmedSearch = search.trim();
99
+ const lines = content.split('\n');
100
+
101
+ for (let i = 0; i < lines.length; i++) {
102
+ const trimmedLine = lines[i].trim();
103
+ if (trimmedLine === trimmedSearch) {
104
+ // Calculate the starting index of this line in the original content
105
+ const startIndex = this.getLineStartIndex(content, i);
106
+ return [startIndex];
107
+ }
108
+ }
109
+
110
+ return [];
111
+ }
112
+
113
+ /**
114
+ * Calculates the starting index of a specific line number in the content.
115
+ * @param content The original content.
116
+ * @param lineNumber The zero-based line number.
117
+ * @returns The starting index of the specified line.
118
+ */
119
+ private getLineStartIndex(content: string, lineNumber: number): number {
120
+ const lines = content.split('\n');
121
+ let index = 0;
122
+ for (let i = 0; i < lineNumber; i++) {
123
+ index += lines[i].length + 1; // +1 for the newline character
124
+ }
125
+ return index;
126
+ }
127
+
128
+ /**
129
+ * Replaces the first occurrence of oldContent with newContent in the content.
130
+ * @param content The original content.
131
+ * @param oldContent The content to be replaced.
132
+ * @param newContent The content to replace with.
133
+ * @returns The content after replacement.
134
+ */
135
+ private replaceContentOnce(content: string, oldContent: string, newContent: string): string {
136
+ const index = content.indexOf(oldContent);
137
+ if (index === -1) { return content; }
138
+ return content.substring(0, index) + newContent + content.substring(index + oldContent.length);
139
+ }
140
+
141
+ /**
142
+ * Replaces all occurrences of oldContent with newContent in the content.
143
+ * @param content The original content.
144
+ * @param oldContent The content to be replaced.
145
+ * @param newContent The content to replace with.
146
+ * @returns The content after all replacements.
147
+ */
148
+ private replaceContentAll(content: string, oldContent: string, newContent: string): string {
149
+ return content.split(oldContent).join(newContent);
150
+ }
151
+ }
@@ -0,0 +1,34 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 TypeFox 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 { interfaces } from 'inversify';
18
+ import { ILogger, Logger, LoggerName, rootLoggerName } from './logger';
19
+ import { LoggerWatcher } from './logger-watcher';
20
+
21
+ export function bindCommonLogger(bind: interfaces.Bind): void {
22
+ bind(LoggerName).toConstantValue(rootLoggerName);
23
+ bind(ILogger).to(Logger).inSingletonScope().when(request => getName(request) === undefined);
24
+ bind(ILogger).toDynamicValue(ctx => {
25
+ const logger = ctx.container.get<ILogger>(ILogger);
26
+ return logger.child(getName(ctx.currentRequest)!);
27
+ }).when(request => getName(request) !== undefined);
28
+ bind(LoggerWatcher).toSelf().inSingletonScope();
29
+ }
30
+
31
+ function getName(request: interfaces.Request): string | undefined {
32
+ const named = request.target.metadata.find(e => e.key === 'named');
33
+ return named ? named.value?.toString() : undefined;
34
+ }
@@ -110,10 +110,12 @@ export namespace ConsoleLogger {
110
110
  console.trace = consoles.get(LogLevel.TRACE)!;
111
111
  console.log = originalConsoleLog;
112
112
  }
113
- export function log(name: string, logLevel: number, message: string, params: any[]): void {
113
+ export function log(name: string, logLevel: number, message: string, params: any[]): string {
114
114
  const console = consoles.get(logLevel) || originalConsoleLog;
115
115
  const severity = (LogLevel.strings.get(logLevel) || 'unknown').toUpperCase();
116
116
  const now = new Date();
117
- console(`${now.toISOString()} ${name} ${severity} ${message}`, ...params);
117
+ const formattedMessage = `${now.toISOString()} ${name} ${severity} ${message}`;
118
+ console(formattedMessage, ...params);
119
+ return formattedMessage;
118
120
  }
119
121
  }