From 34602dbbb2b8dcb3ade61dc1f678811cd9f48ec5 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 5 May 2025 16:44:07 -0700 Subject: [PATCH 01/90] mcp: add validation message for incomplete url (#248174) Fixes #247597 --- src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts index 2d3e09b6a4d..3d2f7817405 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts @@ -123,6 +123,8 @@ export const mcpServerSchema: IJSONSchema = { url: { type: 'string', format: 'uri', + pattern: '^https?:\\/\\/.+', + patternErrorMessage: localize('app.mcp.json.url.pattern', "The URL must start with 'http://' or 'https://'."), description: localize('app.mcp.json.url', "The URL of the Streamable HTTP or SSE endpoint.") }, headers: { From 06066c5cb6f6ace68c665b6ca54de34e73bb0103 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 5 May 2025 16:44:32 -0700 Subject: [PATCH 02/90] tools: fix focus/click handling in tool confirmation (#248177) Make the expandable header a proper Button. Fixes #247966 --- src/vs/base/browser/ui/button/button.ts | 2 ++ .../chatToolInputOutputContentPart.ts | 32 +++++++++---------- .../media/chatConfirmationWidget.css | 23 ++++++++++--- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index aea9be08fcc..d276da47dc7 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -605,6 +605,8 @@ export class ButtonWithIcon extends Button { private _iconElement: HTMLElement; private _mdlabelElement: HTMLElement; + public get labelElement() { return this._mdlabelElement; } + constructor(container: HTMLElement, options: IButtonOptions) { super(container, options); diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts index b354718cfbb..bd6118971ad 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from '../../../../../base/browser/dom.js'; -import { Button } from '../../../../../base/browser/ui/button/button.js'; +import { ButtonWithIcon } from '../../../../../base/browser/ui/button/button.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter } from '../../../../../base/common/event.js'; import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; @@ -86,39 +86,36 @@ export class ChatCollapsibleInputOutputContentPart extends Disposable { ) { super(); - const elements = dom.h('.chat-confirmation-widget@root', [ - dom.h('.chat-confirmation-widget-title.expandable@titleContainer', [ - dom.h('.chat-confirmation-widget-expando@expando'), - dom.h('.chat-confirmation-widget-title-inner@title'), - dom.h('.chat-confirmation-widget-title-icon@icon'), - ]), - dom.h('.chat-confirmation-widget-message@message'), - ]); + const titleEl = dom.h('.chat-confirmation-widget-title-inner'); + const iconEl = dom.h('.chat-confirmation-widget-title-icon'); + const elements = dom.h('.chat-confirmation-widget'); this.domNode = elements.root; const titlePart = this._titlePart = this._register(_instantiationService.createInstance( ChatQueryTitlePart, - elements.title, + titleEl.root, title, subtitle, _instantiationService.createInstance(MarkdownRenderer, {}), )); - this._register(titlePart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); + const spacer = document.createElement('span'); spacer.style.flexGrow = '1'; - elements.title.appendChild(spacer); + + const btn = this._register(new ButtonWithIcon(elements.root, {})); + btn.element.classList.add('chat-confirmation-widget-title', 'monaco-text-button'); + btn.labelElement.append(titleEl.root, iconEl.root); + const check = dom.h(isError ? ThemeIcon.asCSSSelector(Codicon.error) : output ? ThemeIcon.asCSSSelector(Codicon.check) : ThemeIcon.asCSSSelector(ThemeIcon.modify(Codicon.loading, 'spin')) ); - elements.icon.appendChild(check.root); + iconEl.root.appendChild(check.root); const expanded = this._expanded = observableValue(this, initiallyExpanded); - const btn = this._register(new Button(elements.expando, {})); - this._register(autorun(r => { const value = expanded.read(r); btn.icon = value ? Codicon.chevronDown : Codicon.chevronRight; @@ -135,9 +132,10 @@ export class ChatCollapsibleInputOutputContentPart extends Disposable { }; this._register(btn.onDidClick(toggle)); - this._register(dom.addDisposableListener(elements.titleContainer, dom.EventType.CLICK, toggle)); - elements.message.appendChild(this.createMessageContents()); + const message = dom.h('.chat-confirmation-widget-message'); + message.root.appendChild(this.createMessageContents()); + elements.root.appendChild(message.root); } private createMessageContents() { diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css b/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css index 76f390053ac..55eb10b6954 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css @@ -19,12 +19,12 @@ .chat-confirmation-widget .chat-confirmation-widget-title { display: flex; align-items: center; - flex-wrap: wrap; width: 100%; border-radius: 3px; padding: 3px 8px; user-select: none; gap: 4px; + border: none; } .chat-confirmation-widget .chat-confirmation-widget-title.expandable { @@ -32,10 +32,6 @@ margin-left: 0; } -.chat-confirmation-widget .chat-confirmation-widget-title.expandable:hover { - background: var(--vscode-toolbar-hoverBackground); -} - .chat-confirmation-widget .chat-confirmation-widget-title-inner { flex-grow: 1; flex-basis: 0; @@ -65,6 +61,23 @@ align-items: center; } +.chat-confirmation-widget .chat-confirmation-widget-title.monaco-button { + &:hover { + background: var(--vscode-toolbar-hoverBackground); + } + + &:active { + background: var(--vscode-toolbar-activeBackground); + } + + .monaco-button-mdlabel { + display: flex; + width: 100%; + text-align: left; + align-items: center; + } +} + .chat-confirmation-widget-message h3 { font-weight: 600; margin: 4px 0 8px; From b151127beb7eb26f61faa904d3f704b7de719476 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 09:24:41 -0700 Subject: [PATCH 03/90] add 'logTime' method decorator --- src/vs/base/common/decorators/logTime.ts | 142 +++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/vs/base/common/decorators/logTime.ts diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts new file mode 100644 index 00000000000..65612f0d25b --- /dev/null +++ b/src/vs/base/common/decorators/logTime.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assertNever } from '../assert.js'; +import { assertDefined } from '../types.js'; +import { Disposable } from '../lifecycle.js'; + +/** + * TODO: @legomushroom + */ +type TLogLevel = 'trace' | 'debug' | 'info' | 'warning' | 'error'; + +/** + * TODO: @legomushroom + */ +interface ILogger { + trace(message: string, ...args: any[]): void; + debug(message: string, ...args: any[]): void; + info(message: string, ...args: any[]): void; + warn(message: string, ...args: any[]): void; + error(message: string | Error, ...args: any[]): void; +} + +/** + * TODO: @legomushroom + */ +type TObjectWithLogger = T & { logService: ILogger }; + +// /** +// * TODO: @legomushroom +// */ +// interface IOptions { +// readonly name?: string; +// readonly logLevel: TLogLevel; +// } + +// /** +// * TODO: @legomushroom +// */ +// const DEFAULT_OPTIONS: IOptions = { +// logLevel: 'trace', +// }; + +/** + * TODO: @legomushroom + */ +export function logTime( + // TODO: @legomushroom + logLevel: TLogLevel = 'info', +) { + return function logExecutionTimeDecorator< + TObject extends TObjectWithLogger, + >( + _proto: TObject, + methodName: string, + descriptor: TypedPropertyDescriptor<(...args: any[]) => any | Promise>, + ) { + const originalMethod = descriptor.value; + + assertDefined( + originalMethod, + `Method '${methodName}' is not defined.`, + ); + + descriptor.value = function ( + this: TObject, + ...args: Parameters + ): ReturnType { + const startTime = performance.now(); + const result = originalMethod.call(this, ...args); + const timeMs = performance.now() - startTime; + + const logOptions: ILogOptions = { + methodName: `${this.constructor.name}.${methodName}`, + logLevel, + timeMs, + }; + + // TODO: @legomushroom + if (result instanceof Promise) { + return result.then((resolved) => { + log(logOptions, this.logService); + return resolved; + }); + } + + log(logOptions, this.logService); + return result; + }; + + return descriptor; + }; +} + +/** + * TODO: @legomushroom + */ +interface ILogOptions { + timeMs: number; + methodName: string; + logLevel: TLogLevel; +} + +/** + * TODO: @legomushroom + */ +const log = ( + options: ILogOptions, + logger: ILogger, +): void => { + const { logLevel, methodName, timeMs } = options; + + // allow-any-unicode-next-line + const message = `[⏱][${methodName}] took ${timeMs.toFixed(2)} ms`; + + if (logLevel === 'trace') { + return logger.trace(message); + } + + if (logLevel === 'debug') { + return logger.debug(message); + } + + if (logLevel === 'info') { + return logger.info(message); + } + + if (logLevel === 'warning') { + return logger.warn(message); + } + + if (logLevel === 'error') { + return logger.error(message); + } + + assertNever( + logLevel, + `Unknown log level '${logLevel}'.`, + ); +}; From bb65678b89322ed18c40f121e2483e6c62cb9ec0 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 09:25:42 -0700 Subject: [PATCH 04/90] use 'logTime' decorator on the 'getAllMetadata' method of the prompts service --- src/vs/base/common/decorators/logTime.ts | 2 +- src/vs/base/test/common/logTime.test.ts | 14 ++++++++++++++ .../common/promptSyntax/service/promptsService.ts | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/vs/base/test/common/logTime.test.ts diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 65612f0d25b..9db36504de8 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -48,7 +48,7 @@ type TObjectWithLogger = T & { logService: ILogger }; */ export function logTime( // TODO: @legomushroom - logLevel: TLogLevel = 'info', + logLevel: TLogLevel = 'trace', ) { return function logExecutionTimeDecorator< TObject extends TObjectWithLogger, diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts new file mode 100644 index 00000000000..d36234d1b3d --- /dev/null +++ b/src/vs/base/test/common/logTime.test.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; + +suite('logTime decorator', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('logs execution time with provided log level', async () => { + + }); +}); diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index ed7d0ca17ed..ad2dc045293 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -19,7 +19,9 @@ import { PromptFilesLocator } from '../utils/promptFilesLocator.js'; import { ITextModel } from '../../../../../../editor/common/model.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { ObjectCache } from '../../../../../../base/common/objectCache.js'; +import { ILogService } from '../../../../../../platform/log/common/log.js'; import { TextModelPromptParser } from '../parsers/textModelPromptParser.js'; +import { logTime } from '../../../../../../base/common/decorators/logTime.js'; import { ILabelService } from '../../../../../../platform/label/common/label.js'; import { IModelService } from '../../../../../../editor/common/services/model.js'; import { PROMPT_FILE_EXTENSION } from '../../../../../../platform/prompts/common/constants.js'; @@ -48,6 +50,8 @@ export class PromptsService extends Disposable implements IPromptsService { @IModelService private readonly modelService: IModelService, @IInstantiationService private readonly initService: IInstantiationService, @IUserDataProfileService private readonly userDataService: IUserDataProfileService, + // TODO: @legomushroom - make protected? + @ILogService public readonly logService: ILogService, ) { super(); @@ -211,6 +215,7 @@ export class PromptsService extends Disposable implements IPromptsService { return [...new ResourceSet(result)]; } + @logTime() public async getAllMetadata( promptUris: readonly URI[], ): Promise { From f4168b6ca498f2a5f71ccbb31c36f1c27e4ab7c4 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 09:56:11 -0700 Subject: [PATCH 05/90] drop 'disposable' object requirement, add basic unit test --- src/vs/base/common/decorators/logTime.ts | 5 +- src/vs/base/test/common/logTime.test.ts | 62 ++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 9db36504de8..24b8df6d071 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -5,7 +5,6 @@ import { assertNever } from '../assert.js'; import { assertDefined } from '../types.js'; -import { Disposable } from '../lifecycle.js'; /** * TODO: @legomushroom @@ -15,7 +14,7 @@ type TLogLevel = 'trace' | 'debug' | 'info' | 'warning' | 'error'; /** * TODO: @legomushroom */ -interface ILogger { +export interface ILogger { trace(message: string, ...args: any[]): void; debug(message: string, ...args: any[]): void; info(message: string, ...args: any[]): void; @@ -51,7 +50,7 @@ export function logTime( logLevel: TLogLevel = 'trace', ) { return function logExecutionTimeDecorator< - TObject extends TObjectWithLogger, + TObject extends TObjectWithLogger, >( _proto: TObject, methodName: string, diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index d36234d1b3d..b8b2ba9035a 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -3,12 +3,74 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import assert from 'assert'; +import * as sinon from 'sinon'; +import { wait } from './testUtils.js'; +import { randomInt } from '../../common/numbers.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; +import { ILogger, logTime } from '../../common/decorators/logTime.js'; +// TODO: @legomushroom +import { mockObject } from '../../../platform/prompts/test/common/utils/mock.js'; suite('logTime decorator', () => { ensureNoDisposablesAreLeakedInTestSuite(); test('logs execution time with provided log level', async () => { + const traceSpy = sinon.spy(); + const mockLogService = mockObject({ + trace(...args) { + traceSpy(...args); + }, + }); + class TestClass { + public logService = mockLogService; + + constructor( + private readonly returnValue: number + ) { } + + @logTime() + public async myMethod(): Promise { + // TODO: @legomushroom - test the timeout + await wait(10); + + return this.returnValue; + } + } + + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); + + const resultPromise = testObject.myMethod(); + + assert( + resultPromise instanceof Promise, + 'My method must return a promise.', + ); + + const result = await resultPromise; + assert.strictEqual( + result, + expectedReturnValue, + 'My method must return correct value.', + ); + + assert( + traceSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = traceSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert( + callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + 'Logger method must be called with correct message.', + ); }); }); From e4008f8fcb2b38f2feda1c1de113bf7bd1e454bc Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 10:13:55 -0700 Subject: [PATCH 06/90] move 'mockObject' utility under common, update its unit tests --- src/vs/base/test/common/logTime.test.ts | 7 +- src/vs/base/test/common/mockObject.test.ts | 101 ++++++++ src/vs/base/test/common/testUtils.ts | 37 +++ .../prompts/test/common/utils/mock.test.ts | 231 ++++++------------ .../prompts/test/common/utils/mock.ts | 38 +-- .../utils/promptFilesLocator.test.ts | 3 +- 6 files changed, 214 insertions(+), 203 deletions(-) create mode 100644 src/vs/base/test/common/mockObject.test.ts diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index b8b2ba9035a..9bbd52e786c 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -5,12 +5,10 @@ import assert from 'assert'; import * as sinon from 'sinon'; -import { wait } from './testUtils.js'; import { randomInt } from '../../common/numbers.js'; +import { mockObject, waitRandom } from './testUtils.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; import { ILogger, logTime } from '../../common/decorators/logTime.js'; -// TODO: @legomushroom -import { mockObject } from '../../../platform/prompts/test/common/utils/mock.js'; suite('logTime decorator', () => { ensureNoDisposablesAreLeakedInTestSuite(); @@ -32,8 +30,7 @@ suite('logTime decorator', () => { @logTime() public async myMethod(): Promise { - // TODO: @legomushroom - test the timeout - await wait(10); + await waitRandom(10); return this.returnValue; } diff --git a/src/vs/base/test/common/mockObject.test.ts b/src/vs/base/test/common/mockObject.test.ts new file mode 100644 index 00000000000..eff91bb91a4 --- /dev/null +++ b/src/vs/base/test/common/mockObject.test.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { typeCheck } from '../../common/types.js'; +import { randomInt } from '../../common/numbers.js'; +import { mockObject, randomBoolean } from './testUtils.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; + + +suite('mockObject', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('• overrides properties and functions', () => { + interface ITestObject { + foo: string; + bar: string; + readonly baz: number; + someMethod(arg: boolean): string; + anotherMethod(arg: number): boolean; + } + + const mock = mockObject({ + bar: 'oh hi!', + baz: 42, + anotherMethod(arg: number): boolean { + return isNaN(arg); + }, + }); + + typeCheck(mock); + + assert.strictEqual( + mock.bar, + 'oh hi!', + 'bar should be overridden', + ); + + assert.strictEqual( + mock.baz, + 42, + 'baz should be overridden', + ); + + assert( + !(mock.anotherMethod(randomInt(100))), + 'Must execute overridden method correctly 1.', + ); + + assert( + mock.anotherMethod(NaN), + 'Must execute overridden method correctly 2.', + ); + + assert.throws(() => { + // property is not overridden so must throw + // eslint-disable-next-line local/code-no-unused-expressions + mock.foo; + }); + + assert.throws(() => { + // function is not overridden so must throw + mock.someMethod(randomBoolean()); + }); + }); + + test('• immutability of the overrides object', () => { + interface ITestObject { + foo: string; + bar: string; + readonly baz: number; + someMethod(arg: boolean): string; + anotherMethod(arg: number): boolean; + } + + const overrides: Partial = { + baz: 4, + }; + const mock = mockObject(overrides); + typeCheck(mock); + + assert.strictEqual( + mock.baz, + 4, + 'baz should be overridden', + ); + + // overrides object must be immutable + assert.throws(() => { + overrides.foo = 'test'; + }); + + assert.throws(() => { + overrides.someMethod = (arg: boolean) => { + return `${arg}__${arg}`; + }; + }); + }); +}); diff --git a/src/vs/base/test/common/testUtils.ts b/src/vs/base/test/common/testUtils.ts index 57b12285900..e184841e421 100644 --- a/src/vs/base/test/common/testUtils.ts +++ b/src/vs/base/test/common/testUtils.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { randomInt } from '../../common/numbers.js'; +import { assertOneOf } from '../../common/types.js'; export function flakySuite(title: string, fn: () => void) /* Suite */ { return suite(title, function () { @@ -50,3 +51,39 @@ export const waitRandom = (maxMs: number, minMs: number = 0): Promise => { export const randomBoolean = (): boolean => { return Math.random() > 0.5; }; + +/** + * Mocks an `TObject` with the provided `overrides`. + * + * If you need to mock an `Service`, please use {@link mockService} + * instead which provides better type safety guarantees for the case. + * + * @throws Reading non-overridden property or function + * on `TObject` throws an error. + */ +export function mockObject( + overrides: Partial, +): TObject { + // ensure that the overrides object cannot be modified afterward + overrides = Object.freeze(overrides); + + const keys = Object.keys(overrides) as (keyof (typeof overrides))[]; + const service = new Proxy( + {}, + { + get: (_target, key: string | number | Symbol) => { + // sanity check for the provided `key` + assertOneOf( + key, + keys, + `The '${key}' is not mocked.`, + ); + + return overrides[key]; + }, + }); + + // note! it's ok to `as TObject` here, because of + // the runtime checks in the `Proxy` getter + return service as (typeof overrides) as TObject; +} diff --git a/src/vs/platform/prompts/test/common/utils/mock.test.ts b/src/vs/platform/prompts/test/common/utils/mock.test.ts index 454db2ff08b..955918d386e 100644 --- a/src/vs/platform/prompts/test/common/utils/mock.test.ts +++ b/src/vs/platform/prompts/test/common/utils/mock.test.ts @@ -4,189 +4,100 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { mockObject } from './mock.js'; +import { mockService } from './mock.js'; import { typeCheck } from '../../../../../base/common/types.js'; import { randomInt } from '../../../../../base/common/numbers.js'; import { randomBoolean } from '../../../../../base/test/common/testUtils.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -suite('mock', () => { +suite('mockService', () => { ensureNoDisposablesAreLeakedInTestSuite(); - suite('• mockObject', () => { - test('• overrides properties and functions', () => { - interface ITestObject { - foo: string; - bar: string; - readonly baz: number; - someMethod(arg: boolean): string; - anotherMethod(arg: number): boolean; - } + test('• overrides properties and functions', () => { + interface ITestService { + readonly _serviceBrand: undefined; + prop1: string; + id: string; + readonly counter: number; + method1(arg: boolean): string; + testMethod2(arg: number): boolean; + } - const mock = mockObject({ - bar: 'oh hi!', - baz: 42, - anotherMethod(arg: number): boolean { - return isNaN(arg); - }, - }); - - typeCheck(mock); - - assert.strictEqual( - mock.bar, - 'oh hi!', - 'bar should be overriden', - ); - - assert.strictEqual( - mock.baz, - 42, - 'baz should be overriden', - ); - - assert( - !(mock.anotherMethod(randomInt(100))), - 'Must execute overriden method correctly 1.', - ); - - assert( - mock.anotherMethod(NaN), - 'Must execute overriden method correctly 2.', - ); - - assert.throws(() => { - // property is not overriden so must throw - // eslint-disable-next-line local/code-no-unused-expressions - mock.foo; - }); - - assert.throws(() => { - // function is not overriden so must throw - mock.someMethod(randomBoolean()); - }); + const mock = mockService({ + id: 'ciao!', + counter: 74, + testMethod2(arg: number): boolean { + return !isNaN(arg); + }, }); - test('• immutability of the overrides object', () => { - interface ITestObject { - foo: string; - bar: string; - readonly baz: number; - someMethod(arg: boolean): string; - anotherMethod(arg: number): boolean; - } + typeCheck(mock); - const overrides: Partial = { - baz: 4, - }; - const mock = mockObject(overrides); - typeCheck(mock); + assert.strictEqual( + mock.id, + 'ciao!', + 'id should be overridden', + ); - assert.strictEqual( - mock.baz, - 4, - 'baz should be overriden', - ); + assert.strictEqual( + mock.counter, + 74, + 'counter should be overridden', + ); - // overrides object must be immutable - assert.throws(() => { - overrides.foo = 'test'; - }); + assert( + mock.testMethod2(randomInt(100)), + 'Must execute overridden method correctly 1.', + ); - assert.throws(() => { - overrides.someMethod = (arg: boolean) => { - return `${arg}__${arg}`; - }; - }); + assert( + !(mock.testMethod2(NaN)), + 'Must execute overridden method correctly 2.', + ); + + assert.throws(() => { + // property is not overridden so must throw + // eslint-disable-next-line local/code-no-unused-expressions + mock.prop1; + }); + + assert.throws(() => { + // function is not overridden so must throw + mock.method1(randomBoolean()); }); }); - suite('• mockService', () => { - test('• overrides properties and functions', () => { - interface ITestService { - readonly _serviceBrand: undefined; - prop1: string; - id: string; - readonly counter: number; - method1(arg: boolean): string; - testMethod2(arg: number): boolean; - } + test('• immutability of the overrides object', () => { + interface ITestService { + readonly _serviceBrand: undefined; + foo: string; + bar: string; + readonly baz: boolean; + someMethod(arg: boolean): string; + anotherMethod(arg: number): boolean; + } - const mock = mockObject({ - id: 'ciao!', - counter: 74, - testMethod2(arg: number): boolean { - return !isNaN(arg); - }, - }); + const overrides: Partial = { + baz: false, + }; + const mock = mockService(overrides); + typeCheck(mock); - typeCheck(mock); + assert.strictEqual( + mock.baz, + false, + 'baz should be overridden', + ); - assert.strictEqual( - mock.id, - 'ciao!', - 'id should be overriden', - ); - - assert.strictEqual( - mock.counter, - 74, - 'counter should be overriden', - ); - - assert( - mock.testMethod2(randomInt(100)), - 'Must execute overriden method correctly 1.', - ); - - assert( - !(mock.testMethod2(NaN)), - 'Must execute overriden method correctly 2.', - ); - - assert.throws(() => { - // property is not overriden so must throw - // eslint-disable-next-line local/code-no-unused-expressions - mock.prop1; - }); - - assert.throws(() => { - // function is not overriden so must throw - mock.method1(randomBoolean()); - }); + // overrides object must be immutable + assert.throws(() => { + overrides.foo = 'test'; }); - test('• immutability of the overrides object', () => { - interface ITestService { - foo: string; - bar: string; - readonly baz: boolean; - someMethod(arg: boolean): string; - anotherMethod(arg: number): boolean; - } - - const overrides: Partial = { - baz: false, + assert.throws(() => { + overrides.someMethod = (arg: boolean) => { + return `${arg}__${arg}`; }; - const mock = mockObject(overrides); - typeCheck(mock); - - assert.strictEqual( - mock.baz, - false, - 'baz should be overriden', - ); - - // overrides object must be immutable - assert.throws(() => { - overrides.foo = 'test'; - }); - - assert.throws(() => { - overrides.someMethod = (arg: boolean) => { - return `${arg}__${arg}`; - }; - }); }); }); }); diff --git a/src/vs/platform/prompts/test/common/utils/mock.ts b/src/vs/platform/prompts/test/common/utils/mock.ts index bb5f7ff7714..9b9a088be78 100644 --- a/src/vs/platform/prompts/test/common/utils/mock.ts +++ b/src/vs/platform/prompts/test/common/utils/mock.ts @@ -3,43 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assertOneOf } from '../../../../../base/common/types.js'; - -/** - * Mocks an `TObject` with the provided `overrides`. - * - * If you need to mock an `Service`, please use {@link mockService} - * instead which provides better type safety guarantees for the case. - * - * @throws Reading non-overridden property or function - * on `TObject` throws an error. - */ -export function mockObject( - overrides: Partial, -): TObject { - // ensure that the overrides object cannot be modified afterward - overrides = Object.freeze(overrides); - - const keys = Object.keys(overrides) as (keyof (typeof overrides))[]; - const service = new Proxy( - {}, - { - get: (_target, key: string | number | Symbol) => { - // sanity check for the provided `key` - assertOneOf( - key, - keys, - `The '${key}' is not mocked.`, - ); - - return overrides[key]; - }, - }); - - // note! it's ok to `as TObject` here, because of - // the runtime checks in the `Proxy` getter - return service as (typeof overrides) as TObject; -} +import { mockObject } from '../../../../../base/test/common/testUtils.js'; /** * Type for any service. diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts index 670cf234a7b..0300640e18e 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts @@ -9,12 +9,13 @@ import { Schemas } from '../../../../../../../base/common/network.js'; import { basename } from '../../../../../../../base/common/resources.js'; import { isWindows } from '../../../../../../../base/common/platform.js'; import { IMockFolder, MockFilesystem } from '../testUtils/mockFilesystem.js'; +import { mockObject } from '../../../../../../../base/test/common/testUtils.js'; import { IFileService } from '../../../../../../../platform/files/common/files.js'; import { PromptsConfig } from '../../../../../../../platform/prompts/common/config.js'; import { FileService } from '../../../../../../../platform/files/common/fileService.js'; +import { mockService } from '../../../../../../../platform/prompts/test/common/utils/mock.js'; import { ILogService, NullLogService } from '../../../../../../../platform/log/common/log.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../base/test/common/utils.js'; -import { mockObject, mockService } from '../../../../../../../platform/prompts/test/common/utils/mock.js'; import { isValidGlob, PromptFilesLocator } from '../../../../common/promptSyntax/utils/promptFilesLocator.js'; import { InMemoryFileSystemProvider } from '../../../../../../../platform/files/common/inMemoryFilesystemProvider.js'; import { TestInstantiationService } from '../../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; From 099108d0a896f10542932132807ad30f7de56757 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 10:22:02 -0700 Subject: [PATCH 07/90] add unit tests for async and sync decorated method cases --- src/vs/base/common/decorators/logTime.ts | 4 +- src/vs/base/test/common/logTime.test.ts | 134 +++++++++++++++++++++-- 2 files changed, 128 insertions(+), 10 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 24b8df6d071..ca7524e9ce0 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -9,7 +9,7 @@ import { assertDefined } from '../types.js'; /** * TODO: @legomushroom */ -type TLogLevel = 'trace' | 'debug' | 'info' | 'warning' | 'error'; +export type TLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error'; /** * TODO: @legomushroom @@ -126,7 +126,7 @@ const log = ( return logger.info(message); } - if (logLevel === 'warning') { + if (logLevel === 'warn') { return logger.warn(message); } diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index 9bbd52e786c..d938882586b 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -8,18 +8,136 @@ import * as sinon from 'sinon'; import { randomInt } from '../../common/numbers.js'; import { mockObject, waitRandom } from './testUtils.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; -import { ILogger, logTime } from '../../common/decorators/logTime.js'; +import { ILogger, logTime, TLogLevel } from '../../common/decorators/logTime.js'; suite('logTime decorator', () => { ensureNoDisposablesAreLeakedInTestSuite(); - test('logs execution time with provided log level', async () => { - const traceSpy = sinon.spy(); + // TODO: @legomushroom - add separators? + suite('async method', () => { + const logLevels: TLogLevel[] = [ + 'trace', 'debug', 'info', 'warn', 'error', + ]; + + for (const logLevel of logLevels) { + test(`'${logLevel}' log level`, async () => { + const logSpy = sinon.spy(); + + const mockLogService = mockObject({ + [logLevel]: logSpy, + }); + class TestClass { + public logService = mockLogService; + + constructor( + private readonly returnValue: number + ) { } + + @logTime(logLevel) + public async myMethod(): Promise { + await waitRandom(10); + + return this.returnValue; + } + } + + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); + + const resultPromise = testObject.myMethod(); + + assert( + resultPromise instanceof Promise, + 'My method must return a promise.', + ); + + const result = await resultPromise; + assert.strictEqual( + result, + expectedReturnValue, + 'My method must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert( + callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + 'Logger method must be called with correct message.', + ); + }); + } + }); + + suite('sync method', () => { + const logLevels: TLogLevel[] = [ + 'trace', 'debug', 'info', 'warn', 'error', + ]; + + for (const logLevel of logLevels) { + test(`'${logLevel}' log level`, async () => { + const logSpy = sinon.spy(); + + const mockLogService = mockObject({ + [logLevel]: logSpy, + }); + class TestClass { + public logService = mockLogService; + + constructor( + private readonly returnValue: number + ) { } + + @logTime(logLevel) + public myMethod(): number { + return this.returnValue; + } + } + + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); + + const result = testObject.myMethod(); + assert.strictEqual( + result, + expectedReturnValue, + 'My method must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert( + callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + 'Logger method must be called with correct message.', + ); + }); + } + }); + + test('uses \'trace\' level by default', async () => { + const logSpy = sinon.spy(); const mockLogService = mockObject({ - trace(...args) { - traceSpy(...args); - }, + trace: logSpy, }); class TestClass { public logService = mockLogService; @@ -54,11 +172,11 @@ suite('logTime decorator', () => { ); assert( - traceSpy.calledOnce, + logSpy.calledOnce, 'The trace logger method must be called.', ); - const callArgs = traceSpy.getCalls()[0].args; + const callArgs = logSpy.getCalls()[0].args; assert( callArgs.length === 1, From 080047d0d4ac1061162b5f16b9ddd4ab6c9ef573 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 10:23:46 -0700 Subject: [PATCH 08/90] improve unit test output formatting --- src/vs/base/test/common/logTime.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index d938882586b..02352c3ec17 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -13,14 +13,13 @@ import { ILogger, logTime, TLogLevel } from '../../common/decorators/logTime.js' suite('logTime decorator', () => { ensureNoDisposablesAreLeakedInTestSuite(); - // TODO: @legomushroom - add separators? - suite('async method', () => { + suite('• async method', () => { const logLevels: TLogLevel[] = [ 'trace', 'debug', 'info', 'warn', 'error', ]; for (const logLevel of logLevels) { - test(`'${logLevel}' log level`, async () => { + test(`• '${logLevel}' log level`, async () => { const logSpy = sinon.spy(); const mockLogService = mockObject({ @@ -78,13 +77,13 @@ suite('logTime decorator', () => { } }); - suite('sync method', () => { + suite('• sync method', () => { const logLevels: TLogLevel[] = [ 'trace', 'debug', 'info', 'warn', 'error', ]; for (const logLevel of logLevels) { - test(`'${logLevel}' log level`, async () => { + test(`• '${logLevel}' log level`, async () => { const logSpy = sinon.spy(); const mockLogService = mockObject({ @@ -133,7 +132,7 @@ suite('logTime decorator', () => { } }); - test('uses \'trace\' level by default', async () => { + test('• uses \'trace\' level by default', async () => { const logSpy = sinon.spy(); const mockLogService = mockObject({ From cb9f6428ffbe350833ec18cbb4067555ee9c7b36 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 10:41:04 -0700 Subject: [PATCH 09/90] add documentation comments --- src/vs/base/common/decorators/logTime.ts | 76 +++++++++++++++++------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index ca7524e9ce0..937ad07387b 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -7,12 +7,12 @@ import { assertNever } from '../assert.js'; import { assertDefined } from '../types.js'; /** - * TODO: @legomushroom + * Type for supported log levels. */ export type TLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error'; /** - * TODO: @legomushroom + * Interface for an object that provides logging methods. */ export interface ILogger { trace(message: string, ...args: any[]): void; @@ -23,30 +23,46 @@ export interface ILogger { } /** - * TODO: @legomushroom + * Type for an object that contains a `logService` property + * with the logging methods. */ type TObjectWithLogger = T & { logService: ILogger }; -// /** -// * TODO: @legomushroom -// */ -// interface IOptions { -// readonly name?: string; -// readonly logLevel: TLogLevel; -// } - -// /** -// * TODO: @legomushroom -// */ -// const DEFAULT_OPTIONS: IOptions = { -// logLevel: 'trace', -// }; - /** - * TODO: @legomushroom + * Decorator allows to log execution time of any method of a class. + * The class must have the `logService` property that provides + * logging methods that the decorator can call. + * + * The decorated method can be asynchronous or synchronous, but + * the timing message is logged only it finishes *successfully*. + * + * @param logLevel Log level to use for the time message. + * + * ## Examples + * + * ```typescript + * class MyClass { + * constructor( + * @LogService public readonly logService: ILogService, + * ) {} + * + * @logTime('info') + * public async myMethod(): Promise { + * // some artificial delay + * await new Promise((resolve) => setTimeout(resolve, 10)); + * + * return 'haalou!'; + * } + * } + * + * const myObject = instantiationService.createInstance(MyClass); + * + * // once the method completes successfully, the information + * // message '[MyClass.myMethod] took 10.00 ms' is logged + * await myObject.myMethod(); + * ``` */ export function logTime( - // TODO: @legomushroom logLevel: TLogLevel = 'trace', ) { return function logExecutionTimeDecorator< @@ -63,6 +79,7 @@ export function logTime( `Method '${methodName}' is not defined.`, ); + // override the decorated method descriptor.value = function ( this: TObject, ...args: Parameters @@ -77,7 +94,7 @@ export function logTime( timeMs, }; - // TODO: @legomushroom + // handle asynchronous decorated methods if (result instanceof Promise) { return result.then((resolved) => { log(logOptions, this.logService); @@ -85,6 +102,7 @@ export function logTime( }); } + // handle synchronous decorated methods log(logOptions, this.logService); return result; }; @@ -94,16 +112,28 @@ export function logTime( } /** - * TODO: @legomushroom + * Options of the {@link log} function. */ interface ILogOptions { + /** + * Method execution time, milliseconds. + */ timeMs: number; + + /** + * Name of the decorated method. + */ methodName: string; + + /** + * Log level to use for the timing message. + */ logLevel: TLogLevel; } /** - * TODO: @legomushroom + * Internal helper to log the timing message with + * provided details and log level. */ const log = ( options: ILogOptions, From 11d5b390f8841967f8ca609ff879865dd2418bde Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 10:49:54 -0700 Subject: [PATCH 10/90] use the 'logTime' decorator on the other potentially-slow methods of the prompts service, improve documentation comments --- src/vs/base/common/decorators/logTime.ts | 5 ++++- .../chat/common/promptSyntax/service/promptsService.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 937ad07387b..6a21a85eaa4 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -43,7 +43,10 @@ type TObjectWithLogger = T & { logService: ILogger }; * ```typescript * class MyClass { * constructor( - * @LogService public readonly logService: ILogService, + * // because we have the interface restrictions on the class + * // which does not support 'private'/'protected' fields, we are + * // forced to use the 'public' modifier here + * \@ILogService public readonly logService: ILogService, * ) {} * * @logTime('info') diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index ad2dc045293..a81f3c64e05 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -173,6 +173,7 @@ export class PromptsService extends Disposable implements IPromptsService { }); } + @logTime() public async findInstructionFilesFor( files: readonly URI[], ): Promise { @@ -241,6 +242,7 @@ export class PromptsService extends Disposable implements IPromptsService { return metadata; } + @logTime() public async getCombinedToolsMetadata( promptUris: readonly URI[], ): Promise { From cbeb2d1109939cc3bee8f52b7373479c41bb609b Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 10:52:04 -0700 Subject: [PATCH 11/90] improve computation of execution time of asynchronous decorated methods --- src/vs/base/common/decorators/logTime.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 6a21a85eaa4..81a3e6a0230 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -89,24 +89,28 @@ export function logTime( ): ReturnType { const startTime = performance.now(); const result = originalMethod.call(this, ...args); - const timeMs = performance.now() - startTime; - - const logOptions: ILogOptions = { - methodName: `${this.constructor.name}.${methodName}`, - logLevel, - timeMs, - }; + const syncTimeMs = performance.now() - startTime; // handle asynchronous decorated methods if (result instanceof Promise) { return result.then((resolved) => { - log(logOptions, this.logService); + const asyncTimeMs = performance.now() - startTime; + + log({ + methodName: `${this.constructor.name}.${methodName}`, + logLevel, + timeMs: asyncTimeMs, + }, this.logService); return resolved; }); } // handle synchronous decorated methods - log(logOptions, this.logService); + log({ + methodName: `${this.constructor.name}.${methodName}`, + logLevel, + timeMs: syncTimeMs, + }, this.logService); return result; }; From 5bcddab2d7e5f28de8183328a1178625b3af5531 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 11:24:11 -0700 Subject: [PATCH 12/90] add 'logExecutionTime' helper and its unit tests in addition to the decorator --- src/vs/base/common/decorators/logTime.ts | 118 ++++--- src/vs/base/test/common/logTime.test.ts | 416 +++++++++++++++-------- 2 files changed, 327 insertions(+), 207 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 81a3e6a0230..92188924499 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -82,36 +82,17 @@ export function logTime( `Method '${methodName}' is not defined.`, ); - // override the decorated method + // override the decorated method with the one that logs + // a timing message after the original method finishes execution descriptor.value = function ( this: TObject, ...args: Parameters ): ReturnType { - const startTime = performance.now(); - const result = originalMethod.call(this, ...args); - const syncTimeMs = performance.now() - startTime; - - // handle asynchronous decorated methods - if (result instanceof Promise) { - return result.then((resolved) => { - const asyncTimeMs = performance.now() - startTime; - - log({ - methodName: `${this.constructor.name}.${methodName}`, - logLevel, - timeMs: asyncTimeMs, - }, this.logService); - return resolved; - }); - } - - // handle synchronous decorated methods - log({ - methodName: `${this.constructor.name}.${methodName}`, - logLevel, - timeMs: syncTimeMs, - }, this.logService); - return result; + return logExecutionTime( + `${this.constructor.name}.${methodName}`, + originalMethod.bind(this, ...args), + getLogFunction(logLevel, this.logService), + ); }; return descriptor; @@ -119,56 +100,66 @@ export function logTime( } /** - * Options of the {@link log} function. + * TODO: @legomushroom */ -interface ILogOptions { - /** - * Method execution time, milliseconds. - */ - timeMs: number; +export const logExecutionTime = ( + blockName: string, + callback: () => T | Promise, + logger: (message: string, ...args: any[]) => void, +): ReturnType => { + const startTime = performance.now(); + const result = callback(); + const syncTimeMs = performance.now() - startTime; - /** - * Name of the decorated method. - */ - methodName: string; + // handle asynchronous decorated methods + if (result instanceof Promise) { + return result.then((resolved) => { + const asyncTimeMs = performance.now() - startTime; - /** - * Log level to use for the timing message. - */ - logLevel: TLogLevel; -} + log( + blockName, + asyncTimeMs, + logger, + ); + return resolved; + }); + } + + // handle synchronous decorated methods + log( + blockName, + syncTimeMs, + logger, + ); + + return result; +}; /** - * Internal helper to log the timing message with - * provided details and log level. + * Gets method of {@link logger} by the provided {@link logLevel}. */ -const log = ( - options: ILogOptions, +const getLogFunction = ( + logLevel: T, logger: ILogger, -): void => { - const { logLevel, methodName, timeMs } = options; - - // allow-any-unicode-next-line - const message = `[⏱][${methodName}] took ${timeMs.toFixed(2)} ms`; - +): ILogger[T] => { if (logLevel === 'trace') { - return logger.trace(message); + return logger.trace; } if (logLevel === 'debug') { - return logger.debug(message); + return logger.debug; } if (logLevel === 'info') { - return logger.info(message); + return logger.info; } if (logLevel === 'warn') { - return logger.warn(message); + return logger.warn; } if (logLevel === 'error') { - return logger.error(message); + return logger.error; } assertNever( @@ -176,3 +167,18 @@ const log = ( `Unknown log level '${logLevel}'.`, ); }; + +/** + * Internal helper to log the timing message with + * provided details and logger. + */ +const log = ( + methodName: string, + timeMs: number, + logger: (message: string, ...args: any[]) => void, +): void => { + return logger( + // allow-any-unicode-next-line + `[⏱][${methodName}] took ${timeMs.toFixed(2)} ms`, + ); +}; diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index 02352c3ec17..3def3bade99 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -8,183 +8,297 @@ import * as sinon from 'sinon'; import { randomInt } from '../../common/numbers.js'; import { mockObject, waitRandom } from './testUtils.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; -import { ILogger, logTime, TLogLevel } from '../../common/decorators/logTime.js'; +import { ILogger, logExecutionTime, logTime, TLogLevel } from '../../common/decorators/logTime.js'; -suite('logTime decorator', () => { +suite('logTime', () => { ensureNoDisposablesAreLeakedInTestSuite(); - suite('• async method', () => { - const logLevels: TLogLevel[] = [ - 'trace', 'debug', 'info', 'warn', 'error', - ]; + suite('• decorator', () => { + suite('• async method', () => { + const logLevels: TLogLevel[] = [ + 'trace', 'debug', 'info', 'warn', 'error', + ]; - for (const logLevel of logLevels) { - test(`• '${logLevel}' log level`, async () => { - const logSpy = sinon.spy(); + for (const logLevel of logLevels) { + test(`• '${logLevel}' log level`, async () => { + const logSpy = sinon.spy(); - const mockLogService = mockObject({ - [logLevel]: logSpy, - }); - class TestClass { - public logService = mockLogService; + const mockLogService = mockObject({ + [logLevel]: logSpy, + }); + class TestClass { + public logService = mockLogService; - constructor( - private readonly returnValue: number - ) { } + constructor( + private readonly returnValue: number + ) { } - @logTime(logLevel) - public async myMethod(): Promise { - await waitRandom(10); + @logTime(logLevel) + public async myMethod(): Promise { + await waitRandom(10); - return this.returnValue; + return this.returnValue; + } } - } - const expectedReturnValue = randomInt(1000); - const testObject = new TestClass(expectedReturnValue); + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); - const resultPromise = testObject.myMethod(); + const resultPromise = testObject.myMethod(); - assert( - resultPromise instanceof Promise, - 'My method must return a promise.', - ); + assert( + resultPromise instanceof Promise, + 'My method must return a promise.', + ); - const result = await resultPromise; - assert.strictEqual( - result, - expectedReturnValue, - 'My method must return correct value.', - ); + const result = await resultPromise; + assert.strictEqual( + result, + expectedReturnValue, + 'Decorator must return correct value.', + ); - assert( - logSpy.calledOnce, - 'The trace logger method must be called.', - ); + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); - const callArgs = logSpy.getCalls()[0].args; + const callArgs = logSpy.getCalls()[0].args; - assert( - callArgs.length === 1, - 'Logger method must be called with correct number of arguments.', - ); + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); - assert( - callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), - 'Logger method must be called with correct message.', - ); - }); - } - }); - - suite('• sync method', () => { - const logLevels: TLogLevel[] = [ - 'trace', 'debug', 'info', 'warn', 'error', - ]; - - for (const logLevel of logLevels) { - test(`• '${logLevel}' log level`, async () => { - const logSpy = sinon.spy(); - - const mockLogService = mockObject({ - [logLevel]: logSpy, + assert( + callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + 'Logger method must be called with correct message.', + ); }); - class TestClass { - public logService = mockLogService; - - constructor( - private readonly returnValue: number - ) { } - - @logTime(logLevel) - public myMethod(): number { - return this.returnValue; - } - } - - const expectedReturnValue = randomInt(1000); - const testObject = new TestClass(expectedReturnValue); - - const result = testObject.myMethod(); - assert.strictEqual( - result, - expectedReturnValue, - 'My method must return correct value.', - ); - - assert( - logSpy.calledOnce, - 'The trace logger method must be called.', - ); - - const callArgs = logSpy.getCalls()[0].args; - - assert( - callArgs.length === 1, - 'Logger method must be called with correct number of arguments.', - ); - - assert( - callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), - 'Logger method must be called with correct message.', - ); - }); - } - }); - - test('• uses \'trace\' level by default', async () => { - const logSpy = sinon.spy(); - - const mockLogService = mockObject({ - trace: logSpy, - }); - class TestClass { - public logService = mockLogService; - - constructor( - private readonly returnValue: number - ) { } - - @logTime() - public async myMethod(): Promise { - await waitRandom(10); - - return this.returnValue; } - } + }); - const expectedReturnValue = randomInt(1000); - const testObject = new TestClass(expectedReturnValue); + suite('• sync method', () => { + const logLevels: TLogLevel[] = [ + 'trace', 'debug', 'info', 'warn', 'error', + ]; - const resultPromise = testObject.myMethod(); + for (const logLevel of logLevels) { + test(`• '${logLevel}' log level`, async () => { + const logSpy = sinon.spy(); - assert( - resultPromise instanceof Promise, - 'My method must return a promise.', - ); + const mockLogService = mockObject({ + [logLevel]: logSpy, + }); + class TestClass { + public logService = mockLogService; - const result = await resultPromise; - assert.strictEqual( - result, - expectedReturnValue, - 'My method must return correct value.', - ); + constructor( + private readonly returnValue: number + ) { } - assert( - logSpy.calledOnce, - 'The trace logger method must be called.', - ); + @logTime(logLevel) + public myMethod(): number { + return this.returnValue; + } + } - const callArgs = logSpy.getCalls()[0].args; + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); - assert( - callArgs.length === 1, - 'Logger method must be called with correct number of arguments.', - ); + const result = testObject.myMethod(); + assert.strictEqual( + result, + expectedReturnValue, + 'Decorator must return correct value.', + ); - assert( - callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), - 'Logger method must be called with correct message.', - ); + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert( + callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + 'Logger method must be called with correct message.', + ); + }); + } + }); + + test('• uses \'trace\' level by default', async () => { + const logSpy = sinon.spy(); + + const mockLogService = mockObject({ + trace: logSpy, + }); + class TestClass { + public logService = mockLogService; + + constructor( + private readonly returnValue: number + ) { } + + @logTime() + public async myMethod(): Promise { + await waitRandom(10); + + return this.returnValue; + } + } + + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); + + const resultPromise = testObject.myMethod(); + + assert( + resultPromise instanceof Promise, + 'My method must return a promise.', + ); + + const result = await resultPromise; + assert.strictEqual( + result, + expectedReturnValue, + 'Decorator must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert( + callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + 'Logger method must be called with correct message.', + ); + }); + }); + + suite('• logExecutionTime helper', () => { + suite('• async function', () => { + const logLevels: TLogLevel[] = [ + 'trace', 'debug', 'info', 'warn', 'error', + ]; + + for (const logLevel of logLevels) { + test(`• '${logLevel}' log level`, async () => { + const logSpy = sinon.spy(); + + const mockLogService = mockObject({ + [logLevel]: logSpy, + }); + + const expectedReturnValue = randomInt(1000); + const resultPromise = logExecutionTime( + 'my-async-function', + async () => { + await waitRandom(10); + + return expectedReturnValue; + }, + mockLogService[logLevel], + ); + + assert( + resultPromise instanceof Promise, + 'Callback function must return a promise.', + ); + + const result = await resultPromise; + assert.strictEqual( + result, + expectedReturnValue, + 'Helper must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + // TODO: @legomushroom - add regex description + const message = callArgs[0].replaceAll(/\s\d+.\d{2}\sms$/gi, ' 0.00 ms'); + // TODO: @legomushroom - port to all otehr places + assert.strictEqual( + message, + '[⏱][my-async-function] took 0.00 ms', + 'Logger message must start with the correct value.', + ); + }); + } + }); + + suite('• sync function', () => { + const logLevels: TLogLevel[] = [ + 'trace', 'debug', 'info', 'warn', 'error', + ]; + + for (const logLevel of logLevels) { + test(`• '${logLevel}' log level`, async () => { + const logSpy = sinon.spy(); + + const mockLogService = mockObject({ + [logLevel]: logSpy, + }); + + const expectedReturnValue = randomInt(1000); + const result = logExecutionTime( + 'my-sync-function', + () => { + return expectedReturnValue; + }, + mockLogService[logLevel], + ); + + assert.strictEqual( + result, + expectedReturnValue, + 'Helper must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + const message = callArgs[0].replaceAll(/\s\d+.\d{2}\sms$/gi, ' 0.00 ms'); + assert.strictEqual( + message, + '[⏱][my-sync-function] took 0.00 ms', + 'Logger message must start with the correct value.', + ); + }); + } + }); }); }); From cfcf78a426c165babab0f8861d25f2c082beea25 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 28 Apr 2025 12:33:24 -0700 Subject: [PATCH 13/90] use the 'logExecutionTime' helper in chat input part, improve unit tests and documentation comments --- src/vs/base/common/decorators/logTime.ts | 43 +++++++++++-- src/vs/base/test/common/logTime.test.ts | 60 ++++++++++++------- .../contrib/chat/browser/chatInputPart.ts | 13 ++-- 3 files changed, 84 insertions(+), 32 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 92188924499..c1cbed10a79 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -34,7 +34,7 @@ type TObjectWithLogger = T & { logService: ILogger }; * logging methods that the decorator can call. * * The decorated method can be asynchronous or synchronous, but - * the timing message is logged only it finishes *successfully*. + * the timing message is logged only if it finishes *successfully*. * * @param logLevel Log level to use for the time message. * @@ -62,7 +62,13 @@ type TObjectWithLogger = T & { logService: ILogger }; * * // once the method completes successfully, the information * // message '[MyClass.myMethod] took 10.00 ms' is logged - * await myObject.myMethod(); + * const result = await myObject.myMethod(); + * + * assert.strictEqual( + * result, + * 'haalou!', + * 'Must yield original return value', + * ); * ``` */ export function logTime( @@ -99,13 +105,40 @@ export function logTime( }; } + /** - * TODO: @legomushroom + * Helper allows to log execution time of code block or function. + * + * The code block or function can be asynchronous or synchronous, but + * the timing message is logged only if it finishes *successfully*. + * + * ## Examples + * + * ```typescript + * const result = logExecutionTime( + * 'my asynchronous block', + * async () => { + * // some artificial delay + * await new Promise((resolve) => setTimeout(resolve, 10)); + * + * return 'haalou!'; + * }, + * this.logService.info, + * } + * + * // once the callback completes successfully, the information + * // message '[MyClass.myMethod] took 10.00 ms' is logged + * assert.strictEqual( + * result, + * 'haalou!', + * 'Must yield original return value', + * ); + * ``` */ export const logExecutionTime = ( blockName: string, callback: () => T | Promise, - logger: (message: string, ...args: any[]) => void, + logger: ILogger[keyof ILogger], ): ReturnType => { const startTime = performance.now(); const result = callback(); @@ -175,7 +208,7 @@ const getLogFunction = ( const log = ( methodName: string, timeMs: number, - logger: (message: string, ...args: any[]) => void, + logger: ILogger[keyof ILogger], ): void => { return logger( // allow-any-unicode-next-line diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index 3def3bade99..cd2f4be8404 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -13,6 +13,27 @@ import { ILogger, logExecutionTime, logTime, TLogLevel } from '../../common/deco suite('logTime', () => { ensureNoDisposablesAreLeakedInTestSuite(); + /** + * Helper that replaces the timing part of a message to + * a predictable constant value so the message can be + * consistently compared in the tests + */ + const cleanupTimingMessage = ( + message: string, + ): string => { + // sanity check on the message type since this function + // can be called with `any` inside these tests + assert( + typeof message === 'string', + `Message must be a string, got '${message}'.`, + ); + + // regex: targets the ' 123.75 ms' part at the end + // of the provided 'message' string + return message + .replaceAll(/\s\d+.\d{2}\sms$/gi, ' 100.50 ms'); + }; + suite('• decorator', () => { suite('• async method', () => { const logLevels: TLogLevel[] = [ @@ -34,7 +55,7 @@ suite('logTime', () => { ) { } @logTime(logLevel) - public async myMethod(): Promise { + public async myAsyncMethod(): Promise { await waitRandom(10); return this.returnValue; @@ -44,7 +65,7 @@ suite('logTime', () => { const expectedReturnValue = randomInt(1000); const testObject = new TestClass(expectedReturnValue); - const resultPromise = testObject.myMethod(); + const resultPromise = testObject.myAsyncMethod(); assert( resultPromise instanceof Promise, @@ -70,8 +91,9 @@ suite('logTime', () => { 'Logger method must be called with correct number of arguments.', ); - assert( - callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + assert.strictEqual( + cleanupTimingMessage(callArgs[0]), + '[⏱][TestClass.myAsyncMethod] took 100.50 ms', 'Logger method must be called with correct message.', ); }); @@ -98,7 +120,7 @@ suite('logTime', () => { ) { } @logTime(logLevel) - public myMethod(): number { + public mySyncMethod(): number { return this.returnValue; } } @@ -106,7 +128,7 @@ suite('logTime', () => { const expectedReturnValue = randomInt(1000); const testObject = new TestClass(expectedReturnValue); - const result = testObject.myMethod(); + const result = testObject.mySyncMethod(); assert.strictEqual( result, expectedReturnValue, @@ -125,8 +147,9 @@ suite('logTime', () => { 'Logger method must be called with correct number of arguments.', ); - assert( - callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + assert.strictEqual( + cleanupTimingMessage(callArgs[0]), + '[⏱][TestClass.mySyncMethod] took 100.50 ms', 'Logger method must be called with correct message.', ); }); @@ -147,7 +170,7 @@ suite('logTime', () => { ) { } @logTime() - public async myMethod(): Promise { + public async myAsyncMethod(): Promise { await waitRandom(10); return this.returnValue; @@ -157,7 +180,7 @@ suite('logTime', () => { const expectedReturnValue = randomInt(1000); const testObject = new TestClass(expectedReturnValue); - const resultPromise = testObject.myMethod(); + const resultPromise = testObject.myAsyncMethod(); assert( resultPromise instanceof Promise, @@ -183,8 +206,9 @@ suite('logTime', () => { 'Logger method must be called with correct number of arguments.', ); - assert( - callArgs[0].startsWith('[⏱][TestClass.myMethod] took '), + assert.strictEqual( + cleanupTimingMessage(callArgs[0]), + '[⏱][TestClass.myAsyncMethod] took 100.50 ms', 'Logger method must be called with correct message.', ); }); @@ -239,12 +263,9 @@ suite('logTime', () => { 'Logger method must be called with correct number of arguments.', ); - // TODO: @legomushroom - add regex description - const message = callArgs[0].replaceAll(/\s\d+.\d{2}\sms$/gi, ' 0.00 ms'); - // TODO: @legomushroom - port to all otehr places assert.strictEqual( - message, - '[⏱][my-async-function] took 0.00 ms', + cleanupTimingMessage(callArgs[0]), + '[⏱][my-async-function] took 100.50 ms', 'Logger message must start with the correct value.', ); }); @@ -291,10 +312,9 @@ suite('logTime', () => { 'Logger method must be called with correct number of arguments.', ); - const message = callArgs[0].replaceAll(/\s\d+.\d{2}\sms$/gi, ' 0.00 ms'); assert.strictEqual( - message, - '[⏱][my-sync-function] took 0.00 ms', + cleanupTimingMessage(callArgs[0]), + '[⏱][my-sync-function] took 100.50 ms', 'Logger message must start with the correct value.', ); }); diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 9914ef3f6c1..b06aef2e185 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -15,6 +15,7 @@ import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/icon import { IAction } from '../../../../base/common/actions.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Codicon } from '../../../../base/common/codicons.js'; +import { logExecutionTime } from '../../../../base/common/decorators/logTime.js'; import { Emitter } from '../../../../base/common/event.js'; import { HistoryNavigator2 } from '../../../../base/common/history.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; @@ -185,13 +186,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge // prompt files may have nested child references to other prompt // files that are resolved asynchronously, hence we need to wait // for the entire prompt instruction tree to be processed - const instructionsStarted = performance.now(); - - // wait for all prompt files resolve precesses to settle - await this.promptInstructionsAttachmentsPart.allSettled(); - - // allow-any-unicode-next-line - this.logService.trace(`[⏱] instructions tree resolved in ${performance.now() - instructionsStarted}ms`); + await logExecutionTime('instructions tree resolve', + this.promptInstructionsAttachmentsPart + .allSettled.bind(this.promptInstructionsAttachmentsPart), + this.logService.trace.bind(this.logService), + ); contextArr .push(...this.promptInstructionsAttachmentsPart.chatAttachments); From 365074a938b25003b67ec7882290dade4699a56c Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 12:12:57 -0700 Subject: [PATCH 14/90] correctly bind log level function context --- src/vs/base/common/decorators/logTime.ts | 10 +++++----- .../chat/common/promptSyntax/service/promptsService.ts | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index c1cbed10a79..49a96c440dc 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -176,23 +176,23 @@ const getLogFunction = ( logger: ILogger, ): ILogger[T] => { if (logLevel === 'trace') { - return logger.trace; + return logger.trace.bind(logger); } if (logLevel === 'debug') { - return logger.debug; + return logger.debug.bind(logger); } if (logLevel === 'info') { - return logger.info; + return logger.info.bind(logger); } if (logLevel === 'warn') { - return logger.warn; + return logger.warn.bind(logger); } if (logLevel === 'error') { - return logger.error; + return logger.error.bind(logger); } assertNever( diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index a81f3c64e05..9e6db318077 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -50,7 +50,6 @@ export class PromptsService extends Disposable implements IPromptsService { @IModelService private readonly modelService: IModelService, @IInstantiationService private readonly initService: IInstantiationService, @IUserDataProfileService private readonly userDataService: IUserDataProfileService, - // TODO: @legomushroom - make protected? @ILogService public readonly logService: ILogService, ) { super(); From ea4ca7d86e2e6653e8e5fb665524feea945f1803 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 13:07:20 -0700 Subject: [PATCH 15/90] post-rebase cleanup --- .../contrib/chat/common/promptSyntax/service/promptsService.ts | 2 +- .../test/common/promptSyntax/service/promptsService.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index 9e6db318077..098cb11a613 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -46,11 +46,11 @@ export class PromptsService extends Disposable implements IPromptsService { private readonly fileLocator: PromptFilesLocator; constructor( + @ILogService public readonly logService: ILogService, @ILabelService private readonly labelService: ILabelService, @IModelService private readonly modelService: IModelService, @IInstantiationService private readonly initService: IInstantiationService, @IUserDataProfileService private readonly userDataService: IUserDataProfileService, - @ILogService public readonly logService: ILogService, ) { super(); diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts index ad4d983b6da..948ed26b1cf 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts @@ -8,6 +8,7 @@ import * as sinon from 'sinon'; import { ChatMode } from '../../../../common/constants.js'; import { URI } from '../../../../../../../base/common/uri.js'; import { MockFilesystem } from '../testUtils/mockFilesystem.js'; +import { pick } from '../../../../../../../base/common/arrays.js'; import { Schemas } from '../../../../../../../base/common/network.js'; import { Range } from '../../../../../../../editor/common/core/range.js'; import { assertDefined } from '../../../../../../../base/common/types.js'; @@ -29,7 +30,6 @@ import { InMemoryFileSystemProvider } from '../../../../../../../platform/files/ import { INSTRUCTION_FILE_EXTENSION, PROMPT_FILE_EXTENSION } from '../../../../../../../platform/prompts/common/constants.js'; import { TestInstantiationService } from '../../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { TestConfigurationService } from '../../../../../../../platform/configuration/test/common/testConfigurationService.js'; -import { pick } from '../../../../../../../base/common/arrays.js'; /** * Helper class to assert the properties of a link. From cc982a183119dd1eef95f46fad212a1f2a9fd2f0 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 15:14:36 -0700 Subject: [PATCH 16/90] rename required decorator logger property --- src/vs/base/common/decorators/logTime.ts | 10 +++++----- src/vs/base/test/common/logTime.test.ts | 12 ++++++------ .../common/promptSyntax/service/promptsService.ts | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 49a96c440dc..50d2bad6d7b 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -7,7 +7,7 @@ import { assertNever } from '../assert.js'; import { assertDefined } from '../types.js'; /** - * Type for supported log levels. + * Type for supported log levels argument of the decorator. */ export type TLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error'; @@ -23,14 +23,14 @@ export interface ILogger { } /** - * Type for an object that contains a `logService` property + * Type for an object that contains a `logger` property * with the logging methods. */ -type TObjectWithLogger = T & { logService: ILogger }; +type TObjectWithLogger = T & { logger: ILogger }; /** * Decorator allows to log execution time of any method of a class. - * The class must have the `logService` property that provides + * The class must have the `logger` property that provides * logging methods that the decorator can call. * * The decorated method can be asynchronous or synchronous, but @@ -97,7 +97,7 @@ export function logTime( return logExecutionTime( `${this.constructor.name}.${methodName}`, originalMethod.bind(this, ...args), - getLogFunction(logLevel, this.logService), + getLogFunction(logLevel, this.logger), ); }; diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index cd2f4be8404..8cfcce3cc99 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -44,11 +44,11 @@ suite('logTime', () => { test(`• '${logLevel}' log level`, async () => { const logSpy = sinon.spy(); - const mockLogService = mockObject({ + const mockLogger = mockObject({ [logLevel]: logSpy, }); class TestClass { - public logService = mockLogService; + public logger = mockLogger; constructor( private readonly returnValue: number @@ -109,11 +109,11 @@ suite('logTime', () => { test(`• '${logLevel}' log level`, async () => { const logSpy = sinon.spy(); - const mockLogService = mockObject({ + const mockLogger = mockObject({ [logLevel]: logSpy, }); class TestClass { - public logService = mockLogService; + public logger = mockLogger; constructor( private readonly returnValue: number @@ -159,11 +159,11 @@ suite('logTime', () => { test('• uses \'trace\' level by default', async () => { const logSpy = sinon.spy(); - const mockLogService = mockObject({ + const mockLogger = mockObject({ trace: logSpy, }); class TestClass { - public logService = mockLogService; + public logger = mockLogger; constructor( private readonly returnValue: number diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index 098cb11a613..cf1efa86e30 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -46,7 +46,7 @@ export class PromptsService extends Disposable implements IPromptsService { private readonly fileLocator: PromptFilesLocator; constructor( - @ILogService public readonly logService: ILogService, + @ILogService public readonly logger: ILogService, @ILabelService private readonly labelService: ILabelService, @IModelService private readonly modelService: IModelService, @IInstantiationService private readonly initService: IInstantiationService, From 3becda3d4cfb73b55d80519ef30fa02226930a68 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 17:13:24 -0700 Subject: [PATCH 17/90] remove 'TLogLevel' interface --- src/vs/base/common/decorators/logTime.ts | 9 ++------- src/vs/base/test/common/logTime.test.ts | 10 +++++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index 50d2bad6d7b..bb92ce70326 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -6,11 +6,6 @@ import { assertNever } from '../assert.js'; import { assertDefined } from '../types.js'; -/** - * Type for supported log levels argument of the decorator. - */ -export type TLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error'; - /** * Interface for an object that provides logging methods. */ @@ -72,7 +67,7 @@ type TObjectWithLogger = T & { logger: ILogger }; * ``` */ export function logTime( - logLevel: TLogLevel = 'trace', + logLevel: keyof ILogger = 'trace', ) { return function logExecutionTimeDecorator< TObject extends TObjectWithLogger, @@ -171,7 +166,7 @@ export const logExecutionTime = ( /** * Gets method of {@link logger} by the provided {@link logLevel}. */ -const getLogFunction = ( +const getLogFunction = ( logLevel: T, logger: ILogger, ): ILogger[T] => { diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index 8cfcce3cc99..f491ef0fc4b 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -8,7 +8,7 @@ import * as sinon from 'sinon'; import { randomInt } from '../../common/numbers.js'; import { mockObject, waitRandom } from './testUtils.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; -import { ILogger, logExecutionTime, logTime, TLogLevel } from '../../common/decorators/logTime.js'; +import { ILogger, logExecutionTime, logTime } from '../../common/decorators/logTime.js'; suite('logTime', () => { ensureNoDisposablesAreLeakedInTestSuite(); @@ -36,7 +36,7 @@ suite('logTime', () => { suite('• decorator', () => { suite('• async method', () => { - const logLevels: TLogLevel[] = [ + const logLevels: (keyof ILogger)[] = [ 'trace', 'debug', 'info', 'warn', 'error', ]; @@ -101,7 +101,7 @@ suite('logTime', () => { }); suite('• sync method', () => { - const logLevels: TLogLevel[] = [ + const logLevels: (keyof ILogger)[] = [ 'trace', 'debug', 'info', 'warn', 'error', ]; @@ -216,7 +216,7 @@ suite('logTime', () => { suite('• logExecutionTime helper', () => { suite('• async function', () => { - const logLevels: TLogLevel[] = [ + const logLevels: (keyof ILogger)[] = [ 'trace', 'debug', 'info', 'warn', 'error', ]; @@ -273,7 +273,7 @@ suite('logTime', () => { }); suite('• sync function', () => { - const logLevels: TLogLevel[] = [ + const logLevels: (keyof ILogger)[] = [ 'trace', 'debug', 'info', 'warn', 'error', ]; From 3cd17090463692d768c1d3f5e09e6a190be43ec6 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 5 May 2025 20:16:37 -0700 Subject: [PATCH 18/90] Delete LanguageModelExtraDataPart (#248180) * Delete LanguageModelExtraDataPart #246980 * Clean up helper types * Plumb non-image data through --- src/vs/base/common/marshallingIds.ts | 1 - .../common/extensionsApiProposals.ts | 2 +- .../workbench/api/common/extHost.api.impl.ts | 1 - .../api/common/extHostTypeConverters.ts | 55 ++++++++++++------- src/vs/workbench/api/common/extHostTypes.ts | 40 ++++---------- .../contrib/chat/common/languageModels.ts | 10 ++-- ...vscode.proposed.languageModelDataPart.d.ts | 39 +++---------- 7 files changed, 60 insertions(+), 88 deletions(-) diff --git a/src/vs/base/common/marshallingIds.ts b/src/vs/base/common/marshallingIds.ts index 9ea80910eeb..272aa3594e5 100644 --- a/src/vs/base/common/marshallingIds.ts +++ b/src/vs/base/common/marshallingIds.ts @@ -27,5 +27,4 @@ export const enum MarshalledId { LanguageModelTextPart, LanguageModelPromptTsxPart, LanguageModelDataPart, - LanguageModelExtraDataPart, } diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index bef924d6d9b..c43841d4784 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -231,7 +231,7 @@ const _allApiProposals = { }, languageModelDataPart: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelDataPart.d.ts', - version: 2 + version: 3 }, languageModelSystem: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts', diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 71f70cb8489..9b9274fdfd6 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1828,7 +1828,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I LanguageModelToolResult: extHostTypes.LanguageModelToolResult, LanguageModelToolResult2: extHostTypes.LanguageModelToolResult2, LanguageModelDataPart: extHostTypes.LanguageModelDataPart, - LanguageModelExtraDataPart: extHostTypes.LanguageModelExtraDataPart, ExtendedLanguageModelToolResult: extHostTypes.ExtendedLanguageModelToolResult, PreparedTerminalToolInvocation: extHostTypes.PreparedTerminalToolInvocation, LanguageModelChatToolMode: extHostTypes.LanguageModelChatToolMode, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 0a8dfb99651..6a964ad5d4b 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -45,7 +45,7 @@ import { IChatRequestVariableEntry, isImageVariableEntry } from '../../contrib/c import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatProgressMessage, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; import { IToolData, IToolResult } from '../../contrib/chat/common/languageModelToolsService.js'; import * as chatProvider from '../../contrib/chat/common/languageModels.js'; -import { IChatResponseDataPart, IChatResponsePromptTsxPart, IChatResponseTextPart } from '../../contrib/chat/common/languageModels.js'; +import { IChatMessageDataPart, IChatResponseDataPart, IChatResponsePromptTsxPart, IChatResponseTextPart } from '../../contrib/chat/common/languageModels.js'; import { DebugTreeItemCollapsibleState, IDebugVisualizationTreeItem } from '../../contrib/debug/common/debug.js'; import * as notebooks from '../../contrib/notebook/common/notebookCommon.js'; import { CellEditType } from '../../contrib/notebook/common/notebookCommon.js'; @@ -2329,12 +2329,14 @@ export namespace LanguageModelChatMessage { } }); return new types.LanguageModelToolResultPart(c.toolCallId, content, c.isError); - } else if (c.type === 'image_url' || c.type === 'extra_data') { + } else if (c.type === 'image_url') { // Non-stable types return undefined; - } else { + } else if (c.type === 'tool_use') { return new types.LanguageModelToolCallPart(c.toolCallId, c.name, c.parameters); } + + return undefined; }).filter(c => c !== undefined); const role = LanguageModelChatMessageRole.to(message.role); @@ -2426,8 +2428,8 @@ export namespace LanguageModelChatMessage2 { return new types.LanguageModelToolResultPart2(c.toolCallId, content, c.isError); } else if (c.type === 'image_url') { return new types.LanguageModelDataPart(c.value.data.buffer, c.value.mimeType); - } else if (c.type === 'extra_data') { - return new types.LanguageModelExtraDataPart(c.kind, c.data); + } else if (c.type === 'data') { + return new types.LanguageModelDataPart(c.data.buffer, c.mimeType); } else { return new types.LanguageModelToolCallPart(c.toolCallId, c.name, c.parameters); } @@ -2479,15 +2481,23 @@ export namespace LanguageModelChatMessage2 { isError: c.isError }; } else if (c instanceof types.LanguageModelDataPart) { - const value: chatProvider.IChatImageURLPart = { - mimeType: c.mimeType as chatProvider.ChatImageMimeType, - data: VSBuffer.wrap(c.data), - }; + if (isImageDataPart(c)) { + const value: chatProvider.IChatImageURLPart = { + mimeType: c.mimeType as chatProvider.ChatImageMimeType, + data: VSBuffer.wrap(c.data), + }; - return { - type: 'image_url', - value: value - }; + return { + type: 'image_url', + value: value + }; + } else { + return { + type: 'data', + mimeType: c.mimeType, + data: VSBuffer.wrap(c.data), + } satisfies IChatMessageDataPart; + } } else if (c instanceof types.LanguageModelToolCallPart) { return { type: 'tool_use', @@ -2500,12 +2510,6 @@ export namespace LanguageModelChatMessage2 { type: 'text', value: c.value }; - } else if (c instanceof types.LanguageModelExtraDataPart) { - return { - type: 'extra_data', - kind: c.kind, - data: c.data - } satisfies chatProvider.IChatMessagePart; } else { if (typeof c !== 'string') { throw new Error('Unexpected chat message content type llm 2'); @@ -2526,6 +2530,19 @@ export namespace LanguageModelChatMessage2 { } } +function isImageDataPart(part: types.LanguageModelDataPart): boolean { + switch (part.mimeType) { + case types.ChatImageMimeType.PNG: + case types.ChatImageMimeType.JPEG: + case types.ChatImageMimeType.GIF: + case types.ChatImageMimeType.WEBP: + case types.ChatImageMimeType.BMP: + return true; + default: + return false; + } +} + export namespace ChatResponseMarkdownPart { export function from(part: vscode.ChatResponseMarkdownPart): Dto { return { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4590e039171..37e3fa81713 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4872,19 +4872,19 @@ export class LanguageModelChatMessage implements vscode.LanguageModelChatMessage export class LanguageModelChatMessage2 implements vscode.LanguageModelChatMessage2 { - static User(content: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelExtraDataPart)[], name?: string): LanguageModelChatMessage2 { + static User(content: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart)[], name?: string): LanguageModelChatMessage2 { return new LanguageModelChatMessage2(LanguageModelChatMessageRole.User, content, name); } - static Assistant(content: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelExtraDataPart)[], name?: string): LanguageModelChatMessage2 { + static Assistant(content: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart)[], name?: string): LanguageModelChatMessage2 { return new LanguageModelChatMessage2(LanguageModelChatMessageRole.Assistant, content, name); } role: vscode.LanguageModelChatMessageRole; - private _content: (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelExtraDataPart)[] = []; + private _content: (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart)[] = []; - set content(value: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelExtraDataPart)[]) { + set content(value: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart)[]) { if (typeof value === 'string') { // we changed this and still support setting content with a string property. this keep the API runtime stable // despite the breaking change in the type definition. @@ -4894,7 +4894,7 @@ export class LanguageModelChatMessage2 implements vscode.LanguageModelChatMessag } } - get content(): (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelExtraDataPart)[] { + get content(): (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart)[] { return this._content; } @@ -4910,7 +4910,7 @@ export class LanguageModelChatMessage2 implements vscode.LanguageModelChatMessag } } - get content2(): (string | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelExtraDataPart)[] | undefined { + get content2(): (string | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart)[] | undefined { return this.content.map(part => { if (part instanceof LanguageModelTextPart) { return part.value; @@ -4921,7 +4921,7 @@ export class LanguageModelChatMessage2 implements vscode.LanguageModelChatMessag name: string | undefined; - constructor(role: vscode.LanguageModelChatMessageRole, content: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelExtraDataPart)[], name?: string) { + constructor(role: vscode.LanguageModelChatMessageRole, content: string | (LanguageModelTextPart | LanguageModelToolResultPart2 | LanguageModelToolCallPart | LanguageModelDataPart)[], name?: string) { this.role = role; this.content = content; this.name = name; @@ -4970,13 +4970,13 @@ export class LanguageModelDataPart implements vscode.LanguageModelDataPart { return new LanguageModelDataPart(data, mimeType as string); } - static json(value: object): vscode.LanguageModelDataPart { + static json(value: object, mime: string = 'text/x-json'): vscode.LanguageModelDataPart { const rawStr = JSON.stringify(value, undefined, '\t'); - return new LanguageModelDataPart(VSBuffer.fromString(rawStr).buffer, 'json'); + return new LanguageModelDataPart(VSBuffer.fromString(rawStr).buffer, mime); } - static text(value: string): vscode.LanguageModelDataPart { - return new LanguageModelDataPart(VSBuffer.fromString(value).buffer, 'text/plain'); + static text(value: string, mime: string = Mimes.text): vscode.LanguageModelDataPart { + return new LanguageModelDataPart(VSBuffer.fromString(value).buffer, mime); } toJSON() { @@ -4996,24 +4996,6 @@ export enum ChatImageMimeType { BMP = 'image/bmp', } -export class LanguageModelExtraDataPart implements vscode.LanguageModelExtraDataPart { - kind: string; - data: any; - - constructor(kind: string, data: any) { - this.kind = kind; - this.data = data; - } - - toJSON() { - return { - $mid: MarshalledId.LanguageModelExtraDataPart, - kind: this.kind, - data: this.data, - }; - } -} - export class LanguageModelPromptTsxPart { value: unknown; diff --git a/src/vs/workbench/contrib/chat/common/languageModels.ts b/src/vs/workbench/contrib/chat/common/languageModels.ts index 62ed7dcdf82..5927d2b1c79 100644 --- a/src/vs/workbench/contrib/chat/common/languageModels.ts +++ b/src/vs/workbench/contrib/chat/common/languageModels.ts @@ -36,10 +36,10 @@ export interface IChatMessageImagePart { value: IChatImageURLPart; } -export interface IChatMessageExtraDataPart { - type: 'extra_data'; - kind: string; - data: any; +export interface IChatMessageDataPart { + type: 'data'; + mimeType: string; + data: VSBuffer; } export interface IChatImageURLPart { @@ -81,7 +81,7 @@ export interface IChatMessageToolResultPart { isError?: boolean; } -export type IChatMessagePart = IChatMessageTextPart | IChatMessageToolResultPart | IChatResponseToolUsePart | IChatMessageImagePart | IChatMessageExtraDataPart; +export type IChatMessagePart = IChatMessageTextPart | IChatMessageToolResultPart | IChatResponseToolUsePart | IChatMessageImagePart | IChatMessageDataPart; export interface IChatMessage { readonly name?: string | undefined; diff --git a/src/vscode-dts/vscode.proposed.languageModelDataPart.d.ts b/src/vscode-dts/vscode.proposed.languageModelDataPart.d.ts index f365ede02e6..902ed64ec97 100644 --- a/src/vscode-dts/vscode.proposed.languageModelDataPart.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModelDataPart.d.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// version: 2 +// version: 3 declare module 'vscode' { @@ -23,7 +23,7 @@ declare module 'vscode' { * @param content The content of the message. * @param name The optional name of a user for the message. */ - static User(content: string | Array, name?: string): LanguageModelChatMessage2; + static User(content: string | Array, name?: string): LanguageModelChatMessage2; /** * Utility to create a new assistant message. @@ -31,7 +31,7 @@ declare module 'vscode' { * @param content The content of the message. * @param name The optional name of a user for the message. */ - static Assistant(content: string | Array, name?: string): LanguageModelChatMessage2; + static Assistant(content: string | Array, name?: string): LanguageModelChatMessage2; /** * The role of this message. @@ -42,7 +42,7 @@ declare module 'vscode' { * A string or heterogeneous array of things that a message can contain as content. Some parts may be message-type * specific for some models. */ - content: Array; + content: Array; /** * The optional name of a user for this message. @@ -56,7 +56,7 @@ declare module 'vscode' { * @param content The content of the message. * @param name The optional name of a user for the message. */ - constructor(role: LanguageModelChatMessageRole, content: string | Array, name?: string); + constructor(role: LanguageModelChatMessageRole, content: string | Array, name?: string); } /** @@ -70,9 +70,9 @@ declare module 'vscode' { */ static image(data: Uint8Array, mimeType: ChatImageMimeType): LanguageModelDataPart; - static json(value: object): LanguageModelDataPart; + static json(value: any, mime?: string): LanguageModelDataPart; - static text(value: string): LanguageModelDataPart; + static text(value: string, mime?: string): LanguageModelDataPart; /** * The mime type which determines how the data property is interpreted. @@ -102,31 +102,6 @@ declare module 'vscode' { BMP = 'image/bmp', } - /** - * Tagging onto this proposal, because otherwise managing two different extensions of LanguageModelChatMessage could be confusing. - * A language model response part containing arbitrary model-specific data, returned from a {@link LanguageModelChatResponse}. - * TODO@API naming, looking at LanguageModelChatRequestOptions.modelOptions, but LanguageModelModelData is not very good. - * LanguageModelOpaqueData from prompt-tsx? - */ - export class LanguageModelExtraDataPart { - /** - * The type of data. The allowed values and data types here are model-specific. - */ - kind: string; - - /** - * Extra model-specific data. - */ - data: any; - - /** - * Construct an extra data part with the given content. - * @param value The image content of the part. - */ - constructor(kind: string, data: any); - } - - /** * The result of a tool call. This is the counterpart of a {@link LanguageModelToolCallPart tool call} and * it can only be included in the content of a User message From 7705a432df64ec41fde5487e9d9f48f97a526888 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 6 May 2025 02:04:31 -0700 Subject: [PATCH 19/90] Small vscode.d.ts typo fix (#248179) --- src/vscode-dts/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index cefcb1a81b3..7a37f98d239 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -20190,7 +20190,7 @@ declare module 'vscode' { /** * A set of options that control the behavior of the language model. These options are specific to the language model - * and need to be lookup in the respective documentation. + * and need to be looked up in the respective documentation. */ modelOptions?: { [name: string]: any }; From a4f6aadf82df658fe63d4bc9bd2220f34a7927a2 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 6 May 2025 12:12:53 +0000 Subject: [PATCH 20/90] Git - disable "unstage selected ranges" and "unstage changes" commands in the editor/peek view (#248202) * Git - disable "unstage selected ranges" command in the editor * Disable the "Unstage Changes" command --- extensions/git/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 3102a464853..21e90d59ee4 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -977,7 +977,7 @@ "command": "git.unstageSelectedRanges", "key": "ctrl+k ctrl+n", "mac": "cmd+k cmd+n", - "when": "editorTextFocus && isInDiffEditor && isInDiffRightEditor && (resourceScheme == file || resourceScheme == git)" + "when": "editorTextFocus && isInDiffEditor && isInDiffRightEditor && resourceScheme == git" }, { "command": "git.revertSelectedRanges", @@ -1082,7 +1082,7 @@ }, { "command": "git.unstageSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && (resourceScheme == file || resourceScheme == git)" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == git" }, { "command": "git.unstageChange", @@ -2238,7 +2238,7 @@ }, { "command": "git.unstageChange", - "when": "config.git.enabled && !git.missing && originalResource =~ /^git\\:.*%22ref%22%3A%22HEAD%22%7D$/" + "when": "false" } ], "timeline/item/context": [ From 03bea2896eec28e91fdb0652708d2e8cdd86bb2d Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 6 May 2025 16:12:24 +0200 Subject: [PATCH 21/90] update notebooks --- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/my-work.github-issues | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 07b5f0c1ca0..cc9420281ed 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"April 2025\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"May 2025\"" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 401fde7e981..68c38b3ca49 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"March 2025\"\n" + "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"May 2025\"\n" }, { "kind": 1, From fa569ced87d7f6536ca35b9f69cea28abea785c7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 May 2025 16:36:36 +0200 Subject: [PATCH 22/90] When Copilot sign in is going to use enterprise, it should say so (fix microsoft/vscode-copilot#16915) (#248068) --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 043193f16d9..cab0f7a028b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -693,6 +693,10 @@ class ChatSetup { private getPrimaryButton(): string { if (this.context.state.entitlement === ChatEntitlement.Unknown) { + if (ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId) { + return localize('setupWithProviderShort', "Sign in with {0}", defaultChat.enterpriseProviderName); + } + return localize('signInButton', "Sign in"); } From 62b206e3e1cd1bd342f496d651a4823c980cb22a Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 07:41:03 -0700 Subject: [PATCH 23/90] change 'logTime' decorator to require public 'logTime()' method implementation --- src/vs/base/common/decorators/logTime.ts | 73 ++++--------------- .../promptSyntax/service/promptsService.ts | 9 ++- 2 files changed, 24 insertions(+), 58 deletions(-) diff --git a/src/vs/base/common/decorators/logTime.ts b/src/vs/base/common/decorators/logTime.ts index bb92ce70326..1ffe4ebbaea 100644 --- a/src/vs/base/common/decorators/logTime.ts +++ b/src/vs/base/common/decorators/logTime.ts @@ -3,48 +3,43 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assertNever } from '../assert.js'; import { assertDefined } from '../types.js'; /** - * Interface for an object that provides logging methods. + * Type for a function that logs a message. */ -export interface ILogger { - trace(message: string, ...args: any[]): void; - debug(message: string, ...args: any[]): void; - info(message: string, ...args: any[]): void; - warn(message: string, ...args: any[]): void; - error(message: string | Error, ...args: any[]): void; -} +export type TLogFunction = (message: string, ...args: any[]) => void; /** * Type for an object that contains a `logger` property * with the logging methods. */ -type TObjectWithLogger = T & { logger: ILogger }; +type TObjectWithLogFunction = T & { logTime: TLogFunction }; /** * Decorator allows to log execution time of any method of a class. - * The class must have the `logger` property that provides - * logging methods that the decorator can call. + * The class must have the `logTime` method that provides logs + * a provided message. * * The decorated method can be asynchronous or synchronous, but * the timing message is logged only if it finishes *successfully*. * - * @param logLevel Log level to use for the time message. - * * ## Examples * * ```typescript * class MyClass { + * public readonly logTime: TLogFunction; + * constructor( * // because we have the interface restrictions on the class * // which does not support 'private'/'protected' fields, we are * // forced to use the 'public' modifier here * \@ILogService public readonly logService: ILogService, - * ) {} + * ) { + * this.logTime = logService.info.bind(logService); + * } * - * @logTime('info') + * @logTime() * public async myMethod(): Promise { * // some artificial delay * await new Promise((resolve) => setTimeout(resolve, 10)); @@ -66,11 +61,9 @@ type TObjectWithLogger = T & { logger: ILogger }; * ); * ``` */ -export function logTime( - logLevel: keyof ILogger = 'trace', -) { +export function logTime() { return function logExecutionTimeDecorator< - TObject extends TObjectWithLogger, + TObject extends TObjectWithLogFunction, >( _proto: TObject, methodName: string, @@ -92,7 +85,7 @@ export function logTime( return logExecutionTime( `${this.constructor.name}.${methodName}`, originalMethod.bind(this, ...args), - getLogFunction(logLevel, this.logger), + this.logTime.bind(this), ); }; @@ -100,7 +93,6 @@ export function logTime( }; } - /** * Helper allows to log execution time of code block or function. * @@ -133,7 +125,7 @@ export function logTime( export const logExecutionTime = ( blockName: string, callback: () => T | Promise, - logger: ILogger[keyof ILogger], + logger: TLogFunction, ): ReturnType => { const startTime = performance.now(); const result = callback(); @@ -163,39 +155,6 @@ export const logExecutionTime = ( return result; }; -/** - * Gets method of {@link logger} by the provided {@link logLevel}. - */ -const getLogFunction = ( - logLevel: T, - logger: ILogger, -): ILogger[T] => { - if (logLevel === 'trace') { - return logger.trace.bind(logger); - } - - if (logLevel === 'debug') { - return logger.debug.bind(logger); - } - - if (logLevel === 'info') { - return logger.info.bind(logger); - } - - if (logLevel === 'warn') { - return logger.warn.bind(logger); - } - - if (logLevel === 'error') { - return logger.error.bind(logger); - } - - assertNever( - logLevel, - `Unknown log level '${logLevel}'.`, - ); -}; - /** * Internal helper to log the timing message with * provided details and logger. @@ -203,7 +162,7 @@ const getLogFunction = ( const log = ( methodName: string, timeMs: number, - logger: ILogger[keyof ILogger], + logger: TLogFunction, ): void => { return logger( // allow-any-unicode-next-line diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index cf1efa86e30..8fc054c5d81 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -21,9 +21,9 @@ import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { ObjectCache } from '../../../../../../base/common/objectCache.js'; import { ILogService } from '../../../../../../platform/log/common/log.js'; import { TextModelPromptParser } from '../parsers/textModelPromptParser.js'; -import { logTime } from '../../../../../../base/common/decorators/logTime.js'; import { ILabelService } from '../../../../../../platform/label/common/label.js'; import { IModelService } from '../../../../../../editor/common/services/model.js'; +import { logTime, TLogFunction } from '../../../../../../base/common/decorators/logTime.js'; import { PROMPT_FILE_EXTENSION } from '../../../../../../platform/prompts/common/constants.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IUserDataProfileService } from '../../../../../services/userDataProfile/common/userDataProfile.js'; @@ -45,6 +45,12 @@ export class PromptsService extends Disposable implements IPromptsService { */ private readonly fileLocator: PromptFilesLocator; + /** + * Function used by the `@logTime` decorator to log + * execution time of some of the decorated methods. + */ + public logTime: TLogFunction; + constructor( @ILogService public readonly logger: ILogService, @ILabelService private readonly labelService: ILabelService, @@ -55,6 +61,7 @@ export class PromptsService extends Disposable implements IPromptsService { super(); this.fileLocator = this.initService.createInstance(PromptFilesLocator); + this.logTime = this.logger.trace.bind(this.logger); // the factory function below creates a new prompt parser object // for the provided model, if no active non-disposed parser exists From 6eaafd38c4882c0a23edfd722a3cf4f29867d340 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 07:44:45 -0700 Subject: [PATCH 24/90] update unit tests --- src/vs/base/test/common/logTime.test.ts | 394 +++++++++++------------- 1 file changed, 172 insertions(+), 222 deletions(-) diff --git a/src/vs/base/test/common/logTime.test.ts b/src/vs/base/test/common/logTime.test.ts index f491ef0fc4b..2e2f71127d9 100644 --- a/src/vs/base/test/common/logTime.test.ts +++ b/src/vs/base/test/common/logTime.test.ts @@ -5,10 +5,10 @@ import assert from 'assert'; import * as sinon from 'sinon'; +import { waitRandom } from './testUtils.js'; import { randomInt } from '../../common/numbers.js'; -import { mockObject, waitRandom } from './testUtils.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; -import { ILogger, logExecutionTime, logTime } from '../../common/decorators/logTime.js'; +import { logExecutionTime, logTime } from '../../common/decorators/logTime.js'; suite('logTime', () => { ensureNoDisposablesAreLeakedInTestSuite(); @@ -35,135 +35,10 @@ suite('logTime', () => { }; suite('• decorator', () => { - suite('• async method', () => { - const logLevels: (keyof ILogger)[] = [ - 'trace', 'debug', 'info', 'warn', 'error', - ]; - - for (const logLevel of logLevels) { - test(`• '${logLevel}' log level`, async () => { - const logSpy = sinon.spy(); - - const mockLogger = mockObject({ - [logLevel]: logSpy, - }); - class TestClass { - public logger = mockLogger; - - constructor( - private readonly returnValue: number - ) { } - - @logTime(logLevel) - public async myAsyncMethod(): Promise { - await waitRandom(10); - - return this.returnValue; - } - } - - const expectedReturnValue = randomInt(1000); - const testObject = new TestClass(expectedReturnValue); - - const resultPromise = testObject.myAsyncMethod(); - - assert( - resultPromise instanceof Promise, - 'My method must return a promise.', - ); - - const result = await resultPromise; - assert.strictEqual( - result, - expectedReturnValue, - 'Decorator must return correct value.', - ); - - assert( - logSpy.calledOnce, - 'The trace logger method must be called.', - ); - - const callArgs = logSpy.getCalls()[0].args; - - assert( - callArgs.length === 1, - 'Logger method must be called with correct number of arguments.', - ); - - assert.strictEqual( - cleanupTimingMessage(callArgs[0]), - '[⏱][TestClass.myAsyncMethod] took 100.50 ms', - 'Logger method must be called with correct message.', - ); - }); - } - }); - - suite('• sync method', () => { - const logLevels: (keyof ILogger)[] = [ - 'trace', 'debug', 'info', 'warn', 'error', - ]; - - for (const logLevel of logLevels) { - test(`• '${logLevel}' log level`, async () => { - const logSpy = sinon.spy(); - - const mockLogger = mockObject({ - [logLevel]: logSpy, - }); - class TestClass { - public logger = mockLogger; - - constructor( - private readonly returnValue: number - ) { } - - @logTime(logLevel) - public mySyncMethod(): number { - return this.returnValue; - } - } - - const expectedReturnValue = randomInt(1000); - const testObject = new TestClass(expectedReturnValue); - - const result = testObject.mySyncMethod(); - assert.strictEqual( - result, - expectedReturnValue, - 'Decorator must return correct value.', - ); - - assert( - logSpy.calledOnce, - 'The trace logger method must be called.', - ); - - const callArgs = logSpy.getCalls()[0].args; - - assert( - callArgs.length === 1, - 'Logger method must be called with correct number of arguments.', - ); - - assert.strictEqual( - cleanupTimingMessage(callArgs[0]), - '[⏱][TestClass.mySyncMethod] took 100.50 ms', - 'Logger method must be called with correct message.', - ); - }); - } - }); - - test('• uses \'trace\' level by default', async () => { + test('• async method', async () => { const logSpy = sinon.spy(); - - const mockLogger = mockObject({ - trace: logSpy, - }); class TestClass { - public logger = mockLogger; + public logTime = logSpy; constructor( private readonly returnValue: number @@ -214,111 +89,186 @@ suite('logTime', () => { }); }); - suite('• logExecutionTime helper', () => { - suite('• async function', () => { - const logLevels: (keyof ILogger)[] = [ - 'trace', 'debug', 'info', 'warn', 'error', - ]; + test('• sync method', async () => { + const logSpy = sinon.spy(); - for (const logLevel of logLevels) { - test(`• '${logLevel}' log level`, async () => { - const logSpy = sinon.spy(); + class TestClass { + public logTime = logSpy; - const mockLogService = mockObject({ - [logLevel]: logSpy, - }); + constructor( + private readonly returnValue: number + ) { } - const expectedReturnValue = randomInt(1000); - const resultPromise = logExecutionTime( - 'my-async-function', - async () => { - await waitRandom(10); - - return expectedReturnValue; - }, - mockLogService[logLevel], - ); - - assert( - resultPromise instanceof Promise, - 'Callback function must return a promise.', - ); - - const result = await resultPromise; - assert.strictEqual( - result, - expectedReturnValue, - 'Helper must return correct value.', - ); - - assert( - logSpy.calledOnce, - 'The trace logger method must be called.', - ); - - const callArgs = logSpy.getCalls()[0].args; - - assert( - callArgs.length === 1, - 'Logger method must be called with correct number of arguments.', - ); - - assert.strictEqual( - cleanupTimingMessage(callArgs[0]), - '[⏱][my-async-function] took 100.50 ms', - 'Logger message must start with the correct value.', - ); - }); + @logTime() + public mySyncMethod(): number { + return this.returnValue; } + } + + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); + + const result = testObject.mySyncMethod(); + assert.strictEqual( + result, + expectedReturnValue, + 'Decorator must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert.strictEqual( + cleanupTimingMessage(callArgs[0]), + '[⏱][TestClass.mySyncMethod] took 100.50 ms', + 'Logger method must be called with correct message.', + ); + }); + + test('• uses \'trace\' level by default', async () => { + const logSpy = sinon.spy(); + + class TestClass { + public logTime = logSpy; + + constructor( + private readonly returnValue: number + ) { } + + @logTime() + public async myAsyncMethod(): Promise { + await waitRandom(10); + + return this.returnValue; + } + } + + const expectedReturnValue = randomInt(1000); + const testObject = new TestClass(expectedReturnValue); + + const resultPromise = testObject.myAsyncMethod(); + + assert( + resultPromise instanceof Promise, + 'My method must return a promise.', + ); + + const result = await resultPromise; + assert.strictEqual( + result, + expectedReturnValue, + 'Decorator must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert.strictEqual( + cleanupTimingMessage(callArgs[0]), + '[⏱][TestClass.myAsyncMethod] took 100.50 ms', + 'Logger method must be called with correct message.', + ); + }); + + suite('• logExecutionTime helper', () => { + test('• async function', async () => { + const logSpy = sinon.spy(); + + const expectedReturnValue = randomInt(1000); + const resultPromise = logExecutionTime( + 'my-async-function', + async () => { + await waitRandom(10); + + return expectedReturnValue; + }, + logSpy, + ); + + assert( + resultPromise instanceof Promise, + 'Callback function must return a promise.', + ); + + const result = await resultPromise; + assert.strictEqual( + result, + expectedReturnValue, + 'Helper must return correct value.', + ); + + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); + + const callArgs = logSpy.getCalls()[0].args; + + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); + + assert.strictEqual( + cleanupTimingMessage(callArgs[0]), + '[⏱][my-async-function] took 100.50 ms', + 'Logger message must start with the correct value.', + ); }); - suite('• sync function', () => { - const logLevels: (keyof ILogger)[] = [ - 'trace', 'debug', 'info', 'warn', 'error', - ]; + test('• sync function', () => { + const logSpy = sinon.spy(); - for (const logLevel of logLevels) { - test(`• '${logLevel}' log level`, async () => { - const logSpy = sinon.spy(); + const expectedReturnValue = randomInt(1000); + const result = logExecutionTime( + 'my-sync-function', + () => { + return expectedReturnValue; + }, + logSpy, + ); - const mockLogService = mockObject({ - [logLevel]: logSpy, - }); + assert.strictEqual( + result, + expectedReturnValue, + 'Helper must return correct value.', + ); - const expectedReturnValue = randomInt(1000); - const result = logExecutionTime( - 'my-sync-function', - () => { - return expectedReturnValue; - }, - mockLogService[logLevel], - ); + assert( + logSpy.calledOnce, + 'The trace logger method must be called.', + ); - assert.strictEqual( - result, - expectedReturnValue, - 'Helper must return correct value.', - ); + const callArgs = logSpy.getCalls()[0].args; - assert( - logSpy.calledOnce, - 'The trace logger method must be called.', - ); + assert( + callArgs.length === 1, + 'Logger method must be called with correct number of arguments.', + ); - const callArgs = logSpy.getCalls()[0].args; - - assert( - callArgs.length === 1, - 'Logger method must be called with correct number of arguments.', - ); - - assert.strictEqual( - cleanupTimingMessage(callArgs[0]), - '[⏱][my-sync-function] took 100.50 ms', - 'Logger message must start with the correct value.', - ); - }); - } + assert.strictEqual( + cleanupTimingMessage(callArgs[0]), + '[⏱][my-sync-function] took 100.50 ms', + 'Logger message must start with the correct value.', + ); }); }); }); From 5f5d2c13f01f8dd966c57c3da9889ad401afbffa Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 07:52:08 -0700 Subject: [PATCH 25/90] fix flaky unit tests (#248221) --- .../test/common/codecs/tokens/compositeToken.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts b/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts index c8c193acc59..405a0ce4b3f 100644 --- a/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts +++ b/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts @@ -27,7 +27,11 @@ suite('CompositeToken', () => { } public override toString(): string { - throw new Error('Method not implemented.'); + const tokenStrings = this.tokens.map((token) => { + return token.toString(); + }); + + return `CompositeToken:\n${tokenStrings.join('\n')})`; } } @@ -254,7 +258,6 @@ suite('CompositeToken', () => { }); }); - /** * Token type for the {@link cloneTokens} and {@link randomTokens} functions. */ From 070a215b043613f279fa5e3126fc7c19d36c4277 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 08:02:28 -0700 Subject: [PATCH 26/90] refactor tokens render code to common utility call --- .../codecs/markdownCodec/parsers/markdownComment.ts | 8 ++------ .../promptSyntax/codecs/parsers/promptAtMentionParser.ts | 4 ++-- .../codecs/parsers/promptSlashCommandParser.ts | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts index 26e78d7f07d..04697569d8e 100644 --- a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts +++ b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Range } from '../../../core/range.js'; +import { BaseToken } from '../../baseToken.js'; import { Dash } from '../../simpleCodec/tokens/dash.js'; -import { pick } from '../../../../../base/common/arrays.js'; import { assert } from '../../../../../base/common/assert.js'; import { MarkdownComment } from '../tokens/markdownComment.js'; import { TSimpleDecoderToken } from '../../simpleCodec/simpleDecoder.js'; @@ -131,13 +131,9 @@ export class MarkdownCommentStart extends ParserBase Date: Tue, 6 May 2025 17:21:02 +0200 Subject: [PATCH 27/90] process - convert explorer to editor (#248120) --- .../diagnostics/common/diagnostics.ts | 5 +- .../diagnostics/node/diagnosticsService.ts | 2 +- src/vs/platform/editor/common/editor.ts | 24 +- .../electron-main/extensionHostStarter.ts | 1 + src/vs/platform/process/common/process.ts | 12 +- .../electron-main/processMainService.ts | 45 +- .../electron-main/sharedProcess.ts | 1 + .../electron-main/electronPtyHostStarter.ts | 1 + .../common/utilityProcessWorkerService.ts | 5 + .../electron-main/utilityProcess.ts | 7 +- .../utilityProcessWorkerMainService.ts | 1 + src/vs/platform/window/common/window.ts | 1 - .../chat/browser/actions/chatMoveActions.ts | 4 +- .../abstractRuntimeExtensionsEditor.ts | 8 +- .../electron-sandbox/process.contribution.ts | 11 +- .../media/processExplorer.css | 59 ++ .../processExplorer.contribution.ts | 73 +++ .../processExplorerControl.ts | 515 ++++++++++++++++++ .../processExplorerEditoInput.ts | 55 ++ .../electron-sandbox/processExplorerEditor.ts | 42 ++ .../electron-sandbox/desktop.contribution.ts | 8 +- .../browser/auxiliaryWindowService.ts | 4 +- .../editor/common/editorGroupFinder.ts | 6 +- .../editor/common/editorGroupsService.ts | 2 +- .../files/electron-sandbox/watcherClient.ts | 3 +- src/vs/workbench/workbench.desktop.main.ts | 1 + 26 files changed, 870 insertions(+), 26 deletions(-) create mode 100644 src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css create mode 100644 src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts create mode 100644 src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts create mode 100644 src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditoInput.ts create mode 100644 src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditor.ts diff --git a/src/vs/platform/diagnostics/common/diagnostics.ts b/src/vs/platform/diagnostics/common/diagnostics.ts index cc8a4cb379b..a1d0573823b 100644 --- a/src/vs/platform/diagnostics/common/diagnostics.ts +++ b/src/vs/platform/diagnostics/common/diagnostics.ts @@ -94,8 +94,9 @@ export interface IWorkspaceInformation extends IWorkspace { rendererSessionId: string; } -export function isRemoteDiagnosticError(x: any): x is IRemoteDiagnosticError { - return !!x.hostName && !!x.errorMessage; +export function isRemoteDiagnosticError(x: unknown): x is IRemoteDiagnosticError { + const candidate = x as IRemoteDiagnosticError | undefined; + return !!candidate?.hostName && !!candidate?.errorMessage; } export class NullDiagnosticsService implements IDiagnosticsService { diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index e523217c108..ee9cb6dad4d 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -486,7 +486,7 @@ export class DiagnosticsService implements IDiagnosticsService { // Format name with indent let name: string; if (isRoot) { - name = item.pid === mainPid ? `${this.productService.applicationName} main` : 'remote agent'; + name = item.pid === mainPid ? this.productService.applicationName : 'remote-server'; } else { if (mapProcessToName.has(item.pid)) { name = mapProcessToName.get(item.pid)!; diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index 7c856237f67..4fb1a9eb9aa 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -7,6 +7,7 @@ import { equals } from '../../../base/common/arrays.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; import { URI } from '../../../base/common/uri.js'; import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js'; +import { IRectangle } from '../../window/common/window.js'; export interface IResolvableEditorModel extends IDisposable { @@ -300,12 +301,25 @@ export interface IEditorOptions { transient?: boolean; /** - * A hint that the editor should have compact chrome when showing if possible. - * - * Note: this currently is only working if AUX_GROUP is specified as target to - * open the editor in a floating window. + * Options that only apply when `AUX_WINDOW_GROUP` is used for opening. */ - compact?: boolean; + auxiliary?: { + + /** + * Define the bounds of the editor window. + */ + bounds?: Partial; + + /** + * Show editor compact, hiding unnecessary elements. + */ + compact?: boolean; + + /** + * Show the editor always on top of other windows. + */ + alwaysOnTop?: boolean; + }; } export interface ITextEditorSelection { diff --git a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts index fa606b291a3..08c5dec4698 100644 --- a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts @@ -108,6 +108,7 @@ export class ExtensionHostStarter extends Disposable implements IDisposable, IEx extHost.start({ ...opts, type: 'extensionHost', + name: 'extension-host', entryPoint: 'vs/workbench/api/node/extensionHostProcess', args: ['--skipWorkspaceStorageLock'], execArgv: opts.execArgv, diff --git a/src/vs/platform/process/common/process.ts b/src/vs/platform/process/common/process.ts index 50f155da42c..8495e91904a 100644 --- a/src/vs/platform/process/common/process.ts +++ b/src/vs/platform/process/common/process.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ProcessItem } from '../../../base/common/processes.js'; import { ISandboxConfiguration } from '../../../base/parts/sandbox/common/sandboxTypes.js'; -import { PerformanceInfo, SystemInfo } from '../../diagnostics/common/diagnostics.js'; +import { IRemoteDiagnosticError, PerformanceInfo, SystemInfo } from '../../diagnostics/common/diagnostics.js'; import { createDecorator } from '../../instantiation/common/instantiation.js'; // Since data sent through the service is serialized to JSON, functions will be lost, so Color objects @@ -57,12 +58,21 @@ export interface ProcessExplorerWindowConfiguration extends ISandboxConfiguratio export const IProcessMainService = createDecorator('processService'); +export interface IResolvedProcessInformation { + readonly pidToNames: [number, string][]; + readonly processes: { name: string; rootProcess: ProcessItem | IRemoteDiagnosticError }[]; +} + export interface IProcessMainService { + readonly _serviceBrand: undefined; + getSystemStatus(): Promise; stopTracing(): Promise; openProcessExplorer(data: ProcessExplorerData): Promise; + resolve(): Promise; + // Used by the process explorer $getSystemInfo(): Promise; $getPerformanceInfo(): Promise; diff --git a/src/vs/platform/process/electron-main/processMainService.ts b/src/vs/platform/process/electron-main/processMainService.ts index df2af9b7a85..be12b2c8938 100644 --- a/src/vs/platform/process/electron-main/processMainService.ts +++ b/src/vs/platform/process/electron-main/processMainService.ts @@ -11,12 +11,12 @@ import { IProcessEnvironment, isMacintosh } from '../../../base/common/platform. import { listProcesses } from '../../../base/node/ps.js'; import { validatedIpcMain } from '../../../base/parts/ipc/electron-main/ipcMain.js'; import { getNLSLanguage, getNLSMessages, localize } from '../../../nls.js'; -import { IDiagnosticsService, isRemoteDiagnosticError, PerformanceInfo, SystemInfo } from '../../diagnostics/common/diagnostics.js'; +import { IDiagnosticsService, IRemoteDiagnosticError, isRemoteDiagnosticError, PerformanceInfo, SystemInfo } from '../../diagnostics/common/diagnostics.js'; import { IDiagnosticsMainService } from '../../diagnostics/electron-main/diagnosticsMainService.js'; import { IDialogMainService } from '../../dialogs/electron-main/dialogMainService.js'; import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js'; import { ICSSDevelopmentService } from '../../cssDev/node/cssDevService.js'; -import { IProcessMainService, ProcessExplorerData, ProcessExplorerWindowConfiguration } from '../common/process.js'; +import { IProcessMainService, IResolvedProcessInformation, ProcessExplorerData, ProcessExplorerWindowConfiguration } from '../common/process.js'; import { ILogService } from '../../log/common/log.js'; import { INativeHostMainService } from '../../native/electron-main/nativeHostMainService.js'; import product from '../../product/common/product.js'; @@ -26,6 +26,7 @@ import { IStateService } from '../../state/node/state.js'; import { UtilityProcess } from '../../utilityProcess/electron-main/utilityProcess.js'; import { zoomLevelToZoomFactor } from '../../window/common/window.js'; import { IWindowState } from '../../window/electron-main/window.js'; +import { ProcessItem } from '../../../base/common/processes.js'; const processExplorerWindowState = 'issue.processExplorerWindowState'; @@ -62,6 +63,46 @@ export class ProcessMainService implements IProcessMainService { this.registerListeners(); } + async resolve(): Promise { + const mainProcessInfo = await this.diagnosticsMainService.getMainDiagnostics(); + + const pidToNames: [number, string][] = []; + for (const window of mainProcessInfo.windows) { + pidToNames.push([window.pid, `window [${window.id}] (${window.title})`]); + } + + for (const { pid, name } of UtilityProcess.getAll()) { + pidToNames.push([pid, name]); + } + + const processes: { name: string; rootProcess: ProcessItem | IRemoteDiagnosticError }[] = []; + + try { + processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(process.pid) }); + + const remoteDiagnostics = await this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: true }); + remoteDiagnostics.forEach(data => { + if (isRemoteDiagnosticError(data)) { + processes.push({ + name: data.hostName, + rootProcess: data + }); + } else { + if (data.processes) { + processes.push({ + name: data.hostName, + rootProcess: data.processes + }); + } + } + }); + } catch (e) { + this.logService.error(`Listing processes failed: ${e}`); + } + + return { pidToNames, processes }; + } + //#region Register Listeners private registerListeners(): void { diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 9e7389c9826..9e653ef16d4 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -169,6 +169,7 @@ export class SharedProcess extends Disposable { this.utilityProcess.start({ type: 'shared-process', + name: 'shared-process', entryPoint: 'vs/code/electron-utility/sharedProcess/sharedProcessMain', payload: this.createSharedProcessConfiguration(), respondToAuthRequestsFromMainProcess: true, diff --git a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts index d4835c5879f..dc1afec78f1 100644 --- a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts +++ b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts @@ -57,6 +57,7 @@ export class ElectronPtyHostStarter extends Disposable implements IPtyHostStarte this.utilityProcess.start({ type: 'ptyHost', + name: 'pty-host', entryPoint: 'vs/platform/terminal/node/ptyHostMain', execArgv, args: ['--logsPath', this._environmentMainService.logsHome.with({ scheme: Schemas.file }).fsPath], diff --git a/src/vs/platform/utilityProcess/common/utilityProcessWorkerService.ts b/src/vs/platform/utilityProcess/common/utilityProcessWorkerService.ts index fe5b5f1dcc6..dd55f49a2d4 100644 --- a/src/vs/platform/utilityProcess/common/utilityProcessWorkerService.ts +++ b/src/vs/platform/utilityProcess/common/utilityProcessWorkerService.ts @@ -15,6 +15,11 @@ export interface IUtilityProcessWorkerProcess { * forked process to identify it easier. */ readonly type: string; + + /** + * A human-readable name for the utility process. + */ + readonly name: string; } export interface IOnDidTerminateUtilityrocessWorkerProcess { diff --git a/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts b/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts index 6378c6d4c0c..937492c6bbb 100644 --- a/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts +++ b/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts @@ -26,6 +26,11 @@ export interface IUtilityProcessConfiguration { */ readonly type: string; + /** + * A human-readable name for the utility process. + */ + readonly name: string; + /** * The entry point to load in the utility process. */ @@ -306,7 +311,7 @@ export class UtilityProcess extends Disposable { this.processPid = process.pid; if (typeof process.pid === 'number') { - UtilityProcess.all.set(process.pid, { pid: process.pid, name: isWindowUtilityProcessConfiguration(configuration) ? `${configuration.type} [${configuration.responseWindowId}]` : configuration.type }); + UtilityProcess.all.set(process.pid, { pid: process.pid, name: isWindowUtilityProcessConfiguration(configuration) ? `${configuration.name} [${configuration.responseWindowId}]` : configuration.name }); } this.log('successfully created', Severity.Info); diff --git a/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts b/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts index 1ce3d5a18a5..83547d425a4 100644 --- a/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts +++ b/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts @@ -126,6 +126,7 @@ class UtilityProcessWorker extends Disposable { return this.utilityProcess.start({ type: this.configuration.process.type, + name: this.configuration.process.name, entryPoint: this.configuration.process.moduleId, parentLifecycleBound: windowPid, windowLifecycleBound: true, diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 3d394c573d0..ddf6d46f7db 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -429,4 +429,3 @@ export function zoomLevelToZoomFactor(zoomLevel = 0): number { export const DEFAULT_WINDOW_SIZE = { width: 1200, height: 800 } as const; export const DEFAULT_AUX_WINDOW_SIZE = { width: 1024, height: 768 } as const; -export const DEFAULT_COMPACT_AUX_WINDOW_SIZE = { width: 640, height: 640 } as const; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts index 804fd736053..3caa15f12e5 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts @@ -101,7 +101,7 @@ async function executeMoveToAction(accessor: ServicesAccessor, moveTo: MoveToNew const widget = (_sessionId ? widgetService.getWidgetBySessionId(_sessionId) : undefined) ?? widgetService.lastFocusedWidget; if (!widget || !widget.viewModel || widget.location !== ChatAgentLocation.Panel) { - await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options: { pinned: true, compact: moveTo === MoveToNewLocation.Window } }, moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP); + await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options: { pinned: true, auxiliary: { compact: true, bounds: { width: 640, height: 640 } } } }, moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP); return; } @@ -111,7 +111,7 @@ async function executeMoveToAction(accessor: ServicesAccessor, moveTo: MoveToNew widget.clear(); await widget.waitForReady(); - const options: IChatEditorOptions = { target: { sessionId }, pinned: true, viewState, compact: moveTo === MoveToNewLocation.Window }; + const options: IChatEditorOptions = { target: { sessionId }, pinned: true, viewState, auxiliary: { compact: true, bounds: { width: 640, height: 640 } } }; await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options }, moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP); } diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index dddffbe2517..64f980a9e27 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -248,13 +248,13 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { const msgContainer = append(desc, $('div.msg')); const actionbar = new ActionBar(desc); - actionbar.onDidRun(({ error }) => error && this._notificationService.error(error)); + const listener = actionbar.onDidRun(({ error }) => error && this._notificationService.error(error)); const timeContainer = append(element, $('.time')); const activationTime = append(timeContainer, $('div.activation-time')); const profileTime = append(timeContainer, $('div.profile-time')); - const disposables = [actionbar]; + const disposables = [actionbar, listener]; return { root, @@ -468,7 +468,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { this._list.splice(0, this._list.length, this._elements || undefined); - this._list.onContextMenu((e) => { + this._register(this._list.onContextMenu((e) => { if (!e.element) { return; } @@ -504,7 +504,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { getAnchor: () => e.anchor, getActions: () => actions }); - }); + })); } public layout(dimension: Dimension): void { diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts b/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts index 75826f51d8e..f336def40a3 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts @@ -16,6 +16,9 @@ import { IProgressService, ProgressLocation } from '../../../../platform/progres import { IProcessMainService } from '../../../../platform/process/common/process.js'; import './processService.js'; import './processMainService.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { AUX_WINDOW_GROUP, IEditorService } from '../../../services/editor/common/editorService.js'; +import { ProcessExplorerEditorInput } from '../../processExplorer/electron-sandbox/processExplorerEditoInput.js'; //#region Commands @@ -34,8 +37,14 @@ class OpenProcessExplorer extends Action2 { override async run(accessor: ServicesAccessor): Promise { const processService = accessor.get(IWorkbenchProcessService); + const configurationService = accessor.get(IConfigurationService); + const editorService = accessor.get(IEditorService); - return processService.openProcessExplorer(); + if (configurationService.getValue('application.useNewProcessExplorer') !== true) { + return processService.openProcessExplorer(); + } + + editorService.openEditor({ resource: ProcessExplorerEditorInput.RESOURCE, options: { pinned: true, auxiliary: { compact: true, bounds: { width: 800, height: 500 }, alwaysOnTop: true } } }, AUX_WINDOW_GROUP); } } registerAction2(OpenProcessExplorer); diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css b/src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css new file mode 100644 index 00000000000..0b617a8ea5a --- /dev/null +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.process-explorer .row { + display: flex; +} + +.process-explorer .row .cell:not(:first-of-type) { + padding-left: 10px; +} + +.process-explorer .row .cell:not(:last-of-type) { + padding-right: 10px; +} + +.process-explorer .row:not(.header) .cell { + border-right: 1px solid var(--vscode-tree-tableColumnsBorder); +} + +.process-explorer .row.header { + font-weight: 600; + border-bottom: 1px solid var(--vscode-tree-tableColumnsBorder); +} + +.process-explorer .row .cell.name { + text-align: left; + flex-grow: 1; + overflow: hidden; + text-overflow: ellipsis; +} + +.process-explorer .row .cell.cpu { + flex: 0 0 60px; +} + +.process-explorer .row .cell.memory { + flex: 0 0 90px; +} + +.process-explorer .row .cell.pid { + flex: 0 0 50px; +} + +.mac:not(.fullscreen) .process-explorer .monaco-list:focus::before { + /* Rounded corners to make focus outline appear properly (unless fullscreen) */ + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} +.mac:not(.fullscreen).macos-bigsur-or-newer .process-explorer .monaco-list:focus::before { + /* macOS Big Sur increased rounded corners size */ + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; +} + +.process-explorer .monaco-list-row:first-of-type { + border-bottom: 1px solid var(--vscode-tree-tableColumnsBorder); +} diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts new file mode 100644 index 00000000000..f1d943c1be6 --- /dev/null +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from '../../../../nls.js'; +import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; +import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; +import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; +import { IEditorSerializer, EditorExtensions, IEditorFactoryRegistry } from '../../../common/editor.js'; +import { EditorInput } from '../../../common/editor/editorInput.js'; +import { IEditorResolverService, RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js'; +import { ProcessExplorerEditorInput } from './processExplorerEditoInput.js'; +import { ProcessExplorerEditor } from './processExplorerEditor.js'; + +class ProcessExplorerEditorContribution implements IWorkbenchContribution { + + static readonly ID = 'workbench.contrib.processExplorerEditor'; + + constructor( + @IEditorResolverService editorResolverService: IEditorResolverService, + @IInstantiationService instantiationService: IInstantiationService + ) { + editorResolverService.registerEditor( + `${ProcessExplorerEditorInput.RESOURCE.scheme}:**/**`, + { + id: ProcessExplorerEditorInput.ID, + label: localize('promptOpenWith.processExplorer.displayName', "Process Explorer"), + priority: RegisteredEditorPriority.exclusive + }, + { + singlePerResource: true, + canSupportResource: resource => resource.scheme === ProcessExplorerEditorInput.RESOURCE.scheme + }, + { + createEditorInput: () => { + return { + editor: instantiationService.createInstance(ProcessExplorerEditorInput), + options: { + pinned: true + } + }; + } + } + ); + } +} + +registerWorkbenchContribution2(ProcessExplorerEditorContribution.ID, ProcessExplorerEditorContribution, WorkbenchPhase.BlockStartup); + +Registry.as(EditorExtensions.EditorPane).registerEditorPane( + EditorPaneDescriptor.create(ProcessExplorerEditor, ProcessExplorerEditor.ID, localize('processExplorer', "Process Explorer")), + [new SyncDescriptor(ProcessExplorerEditorInput)] +); + +class ProcessExplorerEditorInputSerializer implements IEditorSerializer { + + canSerialize(editorInput: EditorInput): boolean { + return true; + } + + serialize(editorInput: EditorInput): string { + return ''; + } + + deserialize(instantiationService: IInstantiationService): EditorInput { + return ProcessExplorerEditorInput.instance; + } +} + +Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer(ProcessExplorerEditorInput.ID, ProcessExplorerEditorInputSerializer); diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts new file mode 100644 index 00000000000..a027b854676 --- /dev/null +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts @@ -0,0 +1,515 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import './media/processExplorer.css'; +import { localize } from '../../../../nls.js'; +import { INativeHostService } from '../../../../platform/native/common/native.js'; +import { $, append, Dimension, getDocument } from '../../../../base/browser/dom.js'; +import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; +import { IDataSource, ITreeRenderer, ITreeNode, ITreeContextMenuEvent } from '../../../../base/browser/ui/tree/tree.js'; +import { ProcessItem } from '../../../../base/common/processes.js'; +import { IRemoteDiagnosticError, isRemoteDiagnosticError } from '../../../../platform/diagnostics/common/diagnostics.js'; +import { ByteSize } from '../../../../platform/files/common/files.js'; +import { KeyCode } from '../../../../base/common/keyCodes.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { WorkbenchDataTree } from '../../../../platform/list/browser/listService.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { IAction, Separator, toAction } from '../../../../base/common/actions.js'; +import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; +import { coalesce } from '../../../../base/common/arrays.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { RenderIndentGuides } from '../../../../base/browser/ui/tree/abstractTree.js'; +import { isWindows } from '../../../../base/common/platform.js'; +import { IProcessMainService } from '../../../../platform/process/common/process.js'; +import { Delayer } from '../../../../base/common/async.js'; + +const DEBUG_FLAGS_PATTERN = /\s--inspect(?:-brk|port)?=(?\d+)?/; +const DEBUG_PORT_PATTERN = /\s--inspect-port=(?\d+)/; + +//#region --- process explorer tree + +interface IProcessTree { + readonly processes: IProcessInformation; +} + +interface IProcessInformation { + readonly processRoots: IMachineProcessInformation[]; +} + +interface IMachineProcessInformation { + readonly name: string; + readonly rootProcess: ProcessItem | IRemoteDiagnosticError; +} + +function isMachineProcessInformation(item: unknown): item is IMachineProcessInformation { + const candidate = item as IMachineProcessInformation | undefined; + + return !!candidate?.name && !!candidate?.rootProcess; +} + +function isProcessInformation(item: unknown): item is IProcessInformation { + const candidate = item as IProcessInformation | undefined; + + return !!candidate?.processRoots; +} + +function isProcessItem(item: unknown): item is ProcessItem { + const candidate = item as ProcessItem | undefined; + + return typeof candidate?.pid === 'number'; +} + +class ProcessListDelegate implements IListVirtualDelegate { + + getHeight() { + return 22; + } + + getTemplateId(element: IProcessInformation | IMachineProcessInformation | ProcessItem | IRemoteDiagnosticError) { + if (isProcessItem(element)) { + return 'process'; + } + + if (isMachineProcessInformation(element)) { + return 'machine'; + } + + if (isRemoteDiagnosticError(element)) { + return 'error'; + } + + if (isProcessInformation(element)) { + return 'header'; + } + + return ''; + } +} + +class ProcessTreeDataSource implements IDataSource { + + hasChildren(element: IProcessTree | IProcessInformation | IMachineProcessInformation | ProcessItem | IRemoteDiagnosticError): boolean { + if (isRemoteDiagnosticError(element)) { + return false; + } + + if (isProcessItem(element)) { + return !!element.children?.length; + } + + return true; + } + + getChildren(element: IProcessTree | IProcessInformation | IMachineProcessInformation | ProcessItem | IRemoteDiagnosticError) { + if (isProcessItem(element)) { + return element.children ?? []; + } + + if (isRemoteDiagnosticError(element)) { + return []; + } + + if (isProcessInformation(element)) { + if (element.processRoots.length > 1) { + return element.processRoots; // If there are multiple process roots, return these, otherwise go directly to the root process + } + + if (element.processRoots.length > 0) { + return [element.processRoots[0].rootProcess]; + } + + return []; + } + + if (isMachineProcessInformation(element)) { + return [element.rootProcess]; + } + + return element.processes ? [element.processes] : []; + } +} + +function createRow(container: HTMLElement) { + const row = append(container, $('.row')); + + const name = append(row, $('.cell.name')); + const cpu = append(row, $('.cell.cpu')); + const memory = append(row, $('.cell.memory')); + const pid = append(row, $('.cell.pid')); + + return { name, cpu, memory, pid }; +} + +interface IProcessRowTemplateData { + readonly name: HTMLElement; +} + +interface IProcessItemTemplateData extends IProcessRowTemplateData { + readonly cpu: HTMLElement; + readonly memory: HTMLElement; + readonly pid: HTMLElement; +} + +class ProcessHeaderTreeRenderer implements ITreeRenderer { + + readonly templateId: string = 'header'; + + renderTemplate(container: HTMLElement): IProcessItemTemplateData { + return createRow(container); + } + + renderElement(node: ITreeNode, index: number, templateData: IProcessItemTemplateData, height: number | undefined): void { + templateData.name.textContent = localize('processName', "Process Name"); + templateData.cpu.textContent = localize('processCpu', "CPU (%)"); + templateData.pid.textContent = localize('processPid', "PID"); + templateData.memory.textContent = localize('processMemory', "Memory (MB)"); + } + + renderTwistie(element: IProcessInformation, twistieElement: HTMLElement): boolean { + return false; + } + + disposeTemplate(templateData: unknown): void { + // Nothing to do + } +} + +class MachineRenderer implements ITreeRenderer { + + readonly templateId: string = 'machine'; + + renderTemplate(container: HTMLElement): IProcessRowTemplateData { + return createRow(container); + } + + renderElement(node: ITreeNode, index: number, templateData: IProcessRowTemplateData, height: number | undefined): void { + templateData.name.textContent = node.element.name; + } + + disposeTemplate(templateData: IProcessRowTemplateData): void { + // Nothing to do + } +} + +class ErrorRenderer implements ITreeRenderer { + + readonly templateId: string = 'error'; + + renderTemplate(container: HTMLElement): IProcessRowTemplateData { + return createRow(container); + } + + renderElement(node: ITreeNode, index: number, templateData: IProcessRowTemplateData, height: number | undefined): void { + templateData.name.textContent = node.element.errorMessage; + } + + disposeTemplate(templateData: IProcessRowTemplateData): void { + // Nothing to do + } +} + +class ProcessRenderer implements ITreeRenderer { + + readonly templateId: string = 'process'; + + constructor(private totalMem: number, private model: ProcessExplorerModel) { } + + renderTemplate(container: HTMLElement): IProcessItemTemplateData { + return createRow(container); + } + + renderElement(node: ITreeNode, index: number, templateData: IProcessItemTemplateData, height: number | undefined): void { + const { element } = node; + + const pid = element.pid.toFixed(0); + + templateData.name.textContent = this.model.getName(element.pid, element.name); + templateData.name.title = element.cmd; + + templateData.cpu.textContent = element.load.toFixed(0); + templateData.pid.textContent = pid; + templateData.pid.parentElement!.id = `pid-${pid}`; + + const memory = isWindows ? element.mem : (this.totalMem * (element.mem / 100)); + templateData.memory.textContent = (memory / ByteSize.MB).toFixed(0); + } + + disposeTemplate(templateData: IProcessItemTemplateData): void { + // Nothing to do + } +} + +class ProcessAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return localize('processExplorer', "Process Explorer"); + } + + getAriaLabel(element: IMachineProcessInformation | ProcessItem | IRemoteDiagnosticError): string | null { + if (isProcessItem(element) || isMachineProcessInformation(element)) { + return element.name; + } + + if (isRemoteDiagnosticError(element)) { + return element.hostName; + } + + return null; + } +} + +class ProcessIdentityProvider implements IIdentityProvider { + + getId(element: IRemoteDiagnosticError | ProcessItem | IMachineProcessInformation): { toString(): string } { + if (isProcessItem(element)) { + return element.pid.toString(); + } + + if (isRemoteDiagnosticError(element)) { + return element.hostName; + } + + if (isProcessInformation(element)) { + return 'processes'; + } + + if (isMachineProcessInformation(element)) { + return element.name; + } + + return 'header'; + } +} + +//#endregion + +export class ProcessExplorerControl extends Disposable { + + private dimensions: Dimension | undefined = undefined; + + private readonly model: ProcessExplorerModel; + private tree: WorkbenchDataTree | undefined; + + private readonly delayer = this._register(new Delayer(1000)); + + constructor( + container: HTMLElement, + @INativeHostService private readonly nativeHostService: INativeHostService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IProductService private readonly productService: IProductService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @ICommandService private readonly commandService: ICommandService, + @IProcessMainService private readonly processMainService: IProcessMainService + ) { + super(); + + this.model = new ProcessExplorerModel(this.productService); + this.create(container); + } + + private async create(container: HTMLElement): Promise { + const { totalmem } = await this.nativeHostService.getOSStatistics(); + this.createProcessTree(container, totalmem); + + this.update(); + } + + private createProcessTree(container: HTMLElement, totalmem: number): void { + container.classList.add('process-explorer'); + container.id = 'process-explorer'; + + const renderers = [ + new ProcessRenderer(totalmem, this.model), + new ProcessHeaderTreeRenderer(), + new MachineRenderer(), + new ErrorRenderer() + ]; + + this.tree = this._register(this.instantiationService.createInstance( + WorkbenchDataTree, + 'processExplorer', + container, + new ProcessListDelegate(), + renderers, + new ProcessTreeDataSource(), + { + accessibilityProvider: new ProcessAccessibilityProvider(), + identityProvider: new ProcessIdentityProvider(), + expandOnlyOnTwistieClick: true, + renderIndentGuides: RenderIndentGuides.OnHover + })); + + this._register(this.tree.onKeyDown(e => this.onTreeKeyDown(e))); + this._register(this.tree.onContextMenu(e => this.onTreeContextMenu(container, e))); + + this.tree.setInput(this.model); + this.layoutTree(); + } + + private async onTreeKeyDown(e: KeyboardEvent): Promise { + const event = new StandardKeyboardEvent(e); + if (event.keyCode === KeyCode.KeyE && event.altKey) { + const selectionPids = this.getSelectedPids(); + await Promise.all(selectionPids.map(pid => this.nativeHostService.killProcess(pid, 'SIGTERM'))); + } + } + + private onTreeContextMenu(container: HTMLElement, e: ITreeContextMenuEvent): void { + if (!isProcessItem(e.element)) { + return; + } + + const item = e.element; + const pid = Number(item.pid); + + const actions: IAction[] = []; + + actions.push(toAction({ id: 'killProcess', label: localize('killProcess', "Kill Process"), run: () => this.nativeHostService.killProcess(pid, 'SIGTERM') })); + actions.push(toAction({ id: 'forceKillProcess', label: localize('forceKillProcess', "Force Kill Process"), run: () => this.nativeHostService.killProcess(pid, 'SIGKILL') })); + + actions.push(new Separator()); + + actions.push(toAction({ + id: 'copy', + label: localize('copy', "Copy"), + run: () => { + const selectionPids = this.getSelectedPids(); + + if (!selectionPids?.includes(pid)) { + selectionPids.length = 0; // If the selection does not contain the right clicked item, copy the right clicked item only. + selectionPids.push(pid); + } + + const rows = selectionPids?.map(e => getDocument(container).getElementById(`pid-${e}`)).filter(e => !!e); + if (rows) { + const text = rows.map(e => e.innerText).filter(e => !!e); + this.nativeHostService.writeClipboardText(text.join('\n')); + } + } + })); + + actions.push(toAction({ + id: 'copyAll', + label: localize('copyAll', "Copy All"), + run: () => { + const processList = getDocument(container).getElementById('process-explorer'); + if (processList) { + this.nativeHostService.writeClipboardText(processList.innerText); + } + } + })); + + if (this.isDebuggable(item.cmd)) { + actions.push(new Separator()); + actions.push(toAction({ id: 'debug', label: localize('debug', "Debug"), run: () => this.attachTo(item) })); + } + + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => actions + }); + } + + private isDebuggable(cmd: string): boolean { + const matches = DEBUG_FLAGS_PATTERN.exec(cmd); + + return (matches && matches.groups!.port !== '0') || cmd.indexOf('node ') >= 0 || cmd.indexOf('node.exe') >= 0; + } + + private attachTo(item: ProcessItem): void { + const config: { type: string; request: string; name: string; port?: number; processId?: string } = { + type: 'node', + request: 'attach', + name: `process ${item.pid}` + }; + + let matches = DEBUG_FLAGS_PATTERN.exec(item.cmd); + if (matches) { + config.port = Number(matches.groups!.port); + } else { + config.processId = String(item.pid); // no port -> try to attach via pid (send SIGUSR1) + } + + // a debug-port=n or inspect-port=n overrides the port + matches = DEBUG_PORT_PATTERN.exec(item.cmd); + if (matches) { + config.port = Number(matches.groups!.port); // override port + } + + this.commandService.executeCommand('debug.startFromConfig', config); + } + + private getSelectedPids(): number[] { + return coalesce(this.tree?.getSelection()?.map(e => { + if (!isProcessItem(e)) { + return undefined; + } + + return e.pid; + }) ?? []); + } + + private async update(): Promise { + const { processes, pidToNames } = await this.processMainService.resolve(); + + this.model.update(processes, pidToNames); + + this.tree?.updateChildren(); + this.layoutTree(); + + this.delayer.trigger(() => this.update()); + } + + focus(): void { + this.tree?.domFocus(); + } + + layout(dimension: Dimension): void { + this.dimensions = dimension; + + this.layoutTree(); + } + + private layoutTree(): void { + if (this.dimensions && this.tree) { + this.tree.layout(this.dimensions.height, this.dimensions.width); + } + } +} + +class ProcessExplorerModel implements IProcessTree { + + processes: IProcessInformation = { processRoots: [] }; + + private readonly mapPidToName = new Map(); + + constructor(@IProductService private productService: IProductService) { } + + update(processRoots: IMachineProcessInformation[], pidToNames: [number, string][]): void { + + // PID to Names + this.mapPidToName.clear(); + + for (const [pid, name] of pidToNames) { + this.mapPidToName.set(pid, name); + } + + // Processes + processRoots.forEach((info, index) => { + if (isProcessItem(info.rootProcess)) { + info.rootProcess.name = index === 0 ? this.productService.applicationName : 'remote-server'; + } + }); + + this.processes = { processRoots }; + } + + getName(pid: number, fallback: string): string { + return this.mapPidToName.get(pid) ?? fallback; + } +} diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditoInput.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditoInput.ts new file mode 100644 index 00000000000..6ef73d347ff --- /dev/null +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditoInput.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from '../../../../base/common/codicons.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; +import { URI } from '../../../../base/common/uri.js'; +import { localize } from '../../../../nls.js'; +import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; +import { EditorInputCapabilities, IUntypedEditorInput } from '../../../common/editor.js'; +import { EditorInput } from '../../../common/editor/editorInput.js'; + +const processExplorerEditorIcon = registerIcon('process-explorer-editor-label-icon', Codicon.serverProcess, localize('processExplorerEditorLabelIcon', 'Icon of the process explorer editor label.')); + +export class ProcessExplorerEditorInput extends EditorInput { + + static readonly ID = 'workbench.editors.processEditorInput'; + + static readonly RESOURCE = URI.from({ + scheme: 'process-explorer', + path: 'default' + }); + + private static _instance: ProcessExplorerEditorInput; + static get instance() { + if (!ProcessExplorerEditorInput._instance || ProcessExplorerEditorInput._instance.isDisposed()) { + ProcessExplorerEditorInput._instance = new ProcessExplorerEditorInput(); + } + + return ProcessExplorerEditorInput._instance; + } + + override get typeId(): string { return ProcessExplorerEditorInput.ID; } + + override get capabilities(): EditorInputCapabilities { return EditorInputCapabilities.Readonly | EditorInputCapabilities.Singleton; } + + readonly resource = ProcessExplorerEditorInput.RESOURCE; + + override getName(): string { + return localize('processExplorerInputName', "Process Explorer"); + } + + override getIcon(): ThemeIcon { + return processExplorerEditorIcon; + } + + override matches(other: EditorInput | IUntypedEditorInput): boolean { + if (super.matches(other)) { + return true; + } + + return other instanceof ProcessExplorerEditorInput; + } +} diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditor.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditor.ts new file mode 100644 index 00000000000..fa160083ae5 --- /dev/null +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditor.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Dimension } from '../../../../base/browser/dom.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IStorageService } from '../../../../platform/storage/common/storage.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { EditorPane } from '../../../browser/parts/editor/editorPane.js'; +import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; +import { ProcessExplorerControl } from './processExplorerControl.js'; + +export class ProcessExplorerEditor extends EditorPane { + + static readonly ID: string = 'workbench.editor.processExplorer'; + + private processExplorerControl: ProcessExplorerControl | undefined = undefined; + + constructor( + group: IEditorGroup, + @ITelemetryService telemetryService: ITelemetryService, + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(ProcessExplorerEditor.ID, group, telemetryService, themeService, storageService); + } + + protected override createEditor(parent: HTMLElement): void { + this.processExplorerControl = this._register(this.instantiationService.createInstance(ProcessExplorerControl, parent)); + } + + override focus(): void { + this.processExplorerControl?.focus(); + } + + override layout(dimension: Dimension): void { + this.processExplorerControl?.layout(dimension); + } +} diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 88e0091fe2c..1b4d7c1dd9d 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -30,6 +30,7 @@ import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from '../../platform/window/electron-sandbox/window.js'; import { DefaultAccountManagementContribution } from '../services/accounts/common/defaultAccount.js'; import { registerWorkbenchContribution2, WorkbenchPhase } from '../common/contributions.js'; +import product from '../../platform/product/common/product.js'; // Actions (function registerActions(): void { @@ -147,7 +148,12 @@ import { registerWorkbenchContribution2, WorkbenchPhase } from '../common/contri 'included': !isWindows, 'scope': ConfigurationScope.APPLICATION, 'markdownDescription': localize('application.shellEnvironmentResolutionTimeout', "Controls the timeout in seconds before giving up resolving the shell environment when the application is not already launched from a terminal. See our [documentation](https://go.microsoft.com/fwlink/?linkid=2149667) for more information.") - } + }, + 'application.useNewProcessExplorer': { + 'type': 'boolean', + 'default': product.quality !== 'stable', // TODO@bpasero decide on a default + 'description': localize('useNewProcessExplorer', "Controls whether a the process explorer opens in a floating window."), + }, } }); diff --git a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts index b11cd64b76e..8ca61d329e1 100644 --- a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts +++ b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts @@ -21,7 +21,7 @@ import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { DEFAULT_AUX_WINDOW_SIZE, DEFAULT_COMPACT_AUX_WINDOW_SIZE, IRectangle, WindowMinimumSize } from '../../../../platform/window/common/window.js'; +import { DEFAULT_AUX_WINDOW_SIZE, IRectangle, WindowMinimumSize } from '../../../../platform/window/common/window.js'; import { BaseWindow } from '../../../browser/window.js'; import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js'; import { IHostService } from '../../host/browser/host.js'; @@ -319,7 +319,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili height: activeWindow.outerHeight }; - const defaultSize = options?.compact ? DEFAULT_COMPACT_AUX_WINDOW_SIZE : DEFAULT_AUX_WINDOW_SIZE; + const defaultSize = DEFAULT_AUX_WINDOW_SIZE; const width = Math.max(options?.bounds?.width ?? defaultSize.width, WindowMinimumSize.WIDTH); const height = Math.max(options?.bounds?.height ?? defaultSize.height, WindowMinimumSize.HEIGHT); diff --git a/src/vs/workbench/services/editor/common/editorGroupFinder.ts b/src/vs/workbench/services/editor/common/editorGroupFinder.ts index 0ae0eaadb1c..afcc959e8b1 100644 --- a/src/vs/workbench/services/editor/common/editorGroupFinder.ts +++ b/src/vs/workbench/services/editor/common/editorGroupFinder.ts @@ -92,7 +92,11 @@ function doFindGroup(input: EditorInputWithOptions | IUntypedEditorInput, prefer // Group: Aux Window else if (preferredGroup === AUX_WINDOW_GROUP) { - group = editorGroupService.createAuxiliaryEditorPart({ compact: options?.compact }).then(group => group.activeGroup); + group = editorGroupService.createAuxiliaryEditorPart({ + bounds: options?.auxiliary?.bounds, + compact: options?.auxiliary?.compact, + alwaysOnTop: options?.auxiliary?.alwaysOnTop + }).then(group => group.activeGroup); } // Group: Unspecified without a specific index to open diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 1388eac0a75..018c4348b9a 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -565,7 +565,7 @@ export interface IEditorGroupsService extends IEditorGroupsContainer { * Opens a new window with a full editor part instantiated * in there at the optional position and size on screen. */ - createAuxiliaryEditorPart(options?: { bounds?: Partial; compact?: boolean }): Promise; + createAuxiliaryEditorPart(options?: { bounds?: Partial; compact?: boolean; alwaysOnTop?: boolean }): Promise; /** * Returns the instantiation service that is scoped to the diff --git a/src/vs/workbench/services/files/electron-sandbox/watcherClient.ts b/src/vs/workbench/services/files/electron-sandbox/watcherClient.ts index 0d59c18c285..2c8554796b3 100644 --- a/src/vs/workbench/services/files/electron-sandbox/watcherClient.ts +++ b/src/vs/workbench/services/files/electron-sandbox/watcherClient.ts @@ -35,7 +35,8 @@ export class UniversalWatcherClient extends AbstractUniversalWatcherClient { // the process automatically when the window closes or reloads. const { client, onDidTerminate } = disposables.add(await this.utilityProcessWorkerWorkbenchService.createWorker({ moduleId: 'vs/platform/files/node/watcher/watcherMain', - type: 'fileWatcher' + type: 'fileWatcher', + name: 'file-watcher' })); // React on unexpected termination of the watcher process diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 801eccfff62..e7c40ead98b 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -122,6 +122,7 @@ import './contrib/issue/electron-sandbox/issue.contribution.js'; // Process import './contrib/issue/electron-sandbox/process.contribution.js'; +import './contrib/processExplorer/electron-sandbox/processExplorer.contribution.js'; // Remote import './contrib/remote/electron-sandbox/remote.contribution.js'; From e65a9a3d46ae05174e4243f1f258c8f8637def6b Mon Sep 17 00:00:00 2001 From: Aaron Munger <2019016+amunger@users.noreply.github.com> Date: Tue, 6 May 2025 08:46:21 -0700 Subject: [PATCH 28/90] check one more async op (#248225) --- extensions/ipynb/src/test/clearOutputs.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/ipynb/src/test/clearOutputs.test.ts b/extensions/ipynb/src/test/clearOutputs.test.ts index 32c041af982..62e40d6110d 100644 --- a/extensions/ipynb/src/test/clearOutputs.test.ts +++ b/extensions/ipynb/src/test/clearOutputs.test.ts @@ -48,7 +48,11 @@ suite(`ipynb Clear Outputs`, () => { ]; const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python'); - const notebookDocument = await vscode.workspace.openNotebookDocument('jupyter-notebook', notebook); + const notebookDocumentPromise = vscode.workspace.openNotebookDocument('jupyter-notebook', notebook); + await raceTimeout(notebookDocumentPromise, 5000, () => { + throw new Error('Timeout waiting for notebook to open'); + }); + const notebookDocument = await notebookDocumentPromise; await raceTimeout(vscode.window.showNotebookDocument(notebookDocument), 20000, () => { throw new Error('Timeout waiting for notebook to open'); }); From c074cf2dd405406eddb068d02094c25443b6aec8 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 09:32:46 -0700 Subject: [PATCH 29/90] ensure that randomly generated composite tokens are always different in the unit test --- .../editor/test/common/codecs/tokens/compositeToken.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts b/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts index 405a0ce4b3f..4d534d4a8ee 100644 --- a/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts +++ b/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts @@ -228,12 +228,12 @@ suite('CompositeToken', () => { // ensure there is at least one composite token const lastToken = tokens[tokens.length - 1]; const compositeToken1 = new TestToken(randomTokens( - randomInt(5, 2), + randomInt(3, 1), lastToken.range.endLineNumber, lastToken.range.endColumn, )); const compositeToken2 = new TestToken(randomTokens( - randomInt(5, 2), + randomInt(6, 4), lastToken.range.endLineNumber, lastToken.range.endColumn, )); From 2eaa2df61a7e00c3eea2d01149ac56afe06b2493 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 6 May 2025 12:49:02 -0400 Subject: [PATCH 30/90] Fix silly command id mistake (#248210) --- .../chat/browser/chatContentParts/chatQuotaExceededPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts index a5b605970d4..fc645564d61 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts @@ -117,7 +117,7 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar button1.label = button1Label; button1.element.classList.add('chat-quota-error-button'); this._register(button1.onDidClick(async () => { - const commandId = chatEntitlementService.entitlement === ChatEntitlement.Limited ? 'workbench.action.chat.upgradePlan' : 'workbench.action.chat.enableOverages'; + const commandId = chatEntitlementService.entitlement === ChatEntitlement.Limited ? 'workbench.action.chat.upgradePlan' : 'workbench.action.chat.manageOverages'; telemetryService.publicLog2('workbenchActionExecuted', { id: commandId, from: 'chat-response' }); await commandService.executeCommand(commandId); From fc4b29ad8b93fe41a90124f1372e0c8b3c87914d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 6 May 2025 19:52:29 +0200 Subject: [PATCH 31/90] align code style (#248231) --- src/vs/base/common/numbers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index ab93ce1897f..89c9f183e6d 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -143,7 +143,7 @@ export function isPointWithinTriangle( * ); * ``` */ -export const randomInt = (max: number, min: number = 0): number => { +export function randomInt(max: number, min: number = 0): number { assert(!isNaN(min), '"min" param is not a number.'); assert(!isNaN(max), '"max" param is not a number.'); @@ -156,7 +156,7 @@ export const randomInt = (max: number, min: number = 0): number => { const randomFloat = delta * Math.random(); return Math.round(min + randomFloat); -}; +} export function randomChance(p: number): boolean { assert(p >= 0 && p <= 1, 'p must be between 0 and 1'); From 6a77a1e832797ea6f554be33694d1d60c70959cd Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 6 May 2025 11:17:41 -0700 Subject: [PATCH 32/90] testing: don't show coverage action in inappropriate places (#248176) Fixes #247608 --- .../testing/browser/codeCoverageDecorations.ts | 12 +++++++++--- .../contrib/testing/common/testingContextKeys.ts | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts index f82948d3106..6d0c07ae2e3 100644 --- a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts @@ -113,6 +113,12 @@ export class CodeCoverageDecorations extends Disposable implements IEditorContri reader => !!fileCoverage.read(reader)?.file.perTestData?.size, )); + this._register(bindContextKey( + TestingContextKeys.hasCoverageInFile, + contextKeyService, + reader => !!fileCoverage.read(reader)?.file, + )); + this._register(autorun(reader => { const c = fileCoverage.read(reader); if (c) { @@ -718,7 +724,7 @@ registerAction2(class ToggleInlineCoverage extends Action2 { icon: testingCoverageReport, menu: [ { id: MenuId.CommandPalette, when: TestingContextKeys.isTestCoverageOpen }, - { id: MenuId.EditorTitle, when: ContextKeyExpr.and(TestingContextKeys.isTestCoverageOpen, TestingContextKeys.coverageToolbarEnabled.notEqualsTo(true)), group: 'navigation' }, + { id: MenuId.EditorTitle, when: ContextKeyExpr.and(TestingContextKeys.hasCoverageInFile, TestingContextKeys.coverageToolbarEnabled.notEqualsTo(true)), group: 'navigation' }, ] }); } @@ -744,7 +750,7 @@ registerAction2(class ToggleCoverageToolbar extends Action2 { menu: [ { id: MenuId.CommandPalette, when: TestingContextKeys.isTestCoverageOpen }, { id: MenuId.StickyScrollContext, when: TestingContextKeys.isTestCoverageOpen }, - { id: MenuId.EditorTitle, when: TestingContextKeys.isTestCoverageOpen, group: 'coverage@1' }, + { id: MenuId.EditorTitle, when: TestingContextKeys.hasCoverageInFile, group: 'coverage@1' }, ] }); } @@ -771,7 +777,7 @@ registerAction2(class FilterCoverageToTestInEditor extends Action2 { { id: MenuId.EditorTitle, when: ContextKeyExpr.and( - TestingContextKeys.isTestCoverageOpen, + TestingContextKeys.hasCoverageInFile, TestingContextKeys.coverageToolbarEnabled.notEqualsTo(true), TestingContextKeys.hasPerTestCoverage, ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), diff --git a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts index 083b6befcde..54c4b311b80 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -23,6 +23,7 @@ export namespace TestingContextKeys { export const activeEditorHasTests = new RawContextKey('testing.activeEditorHasTests', false, { type: 'boolean', description: localize('testing.activeEditorHasTests', 'Indicates whether any tests are present in the current editor') }); export const cursorInsideTestRange = new RawContextKey('testing.cursorInsideTestRange', false, { type: 'boolean', description: localize('testing.cursorInsideTestRange', 'Whether the cursor is currently inside a test range') }); export const isTestCoverageOpen = new RawContextKey('testing.isTestCoverageOpen', false, { type: 'boolean', description: localize('testing.isTestCoverageOpen', 'Indicates whether a test coverage report is open') }); + export const hasCoverageInFile = new RawContextKey('testing.hasCoverageInFile', false, { type: 'boolean', description: localize('testing.hasCoverageInFile', 'Indicates coverage has been reported in the curent editor.') }); export const hasPerTestCoverage = new RawContextKey('testing.hasPerTestCoverage', false, { type: 'boolean', description: localize('testing.hasPerTestCoverage', 'Indicates whether per-test coverage is available') }); export const isCoverageFilteredToTest = new RawContextKey('testing.isCoverageFilteredToTest', false, { type: 'boolean', description: localize('testing.isCoverageFilteredToTest', 'Indicates whether coverage has been filterd to a single test') }); export const coverageToolbarEnabled = new RawContextKey('testing.coverageToolbarEnabled', true, { type: 'boolean', description: localize('testing.coverageToolbarEnabled', 'Indicates whether the coverage toolbar is enabled') }); From a3e8152d0ab407a6f9e0d09b72ca47392b5b366d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 6 May 2025 12:18:41 -0700 Subject: [PATCH 33/90] mcp: final polish for MCP API (#248235) --- .../platform/extensions/common/extensions.ts | 2 +- src/vs/workbench/api/common/extHostMcp.ts | 11 ++-- .../contrib/mcp/common/mcpConfiguration.ts | 9 +++- ...ode.proposed.mcpConfigurationProvider.d.ts | 54 ++++++++++++++----- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 8e68cbe06d1..f0a4ef36557 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -216,7 +216,7 @@ export interface IExtensionContributions { readonly debugVisualizers?: IDebugVisualizationContribution[]; readonly chatParticipants?: ReadonlyArray; readonly languageModelTools?: ReadonlyArray; - readonly modelContextServerCollections?: ReadonlyArray; + readonly mcpServerDefinitionProviders?: ReadonlyArray; } export interface IExtensionCapabilities { diff --git a/src/vs/workbench/api/common/extHostMcp.ts b/src/vs/workbench/api/common/extHostMcp.ts index 7442f2d98ec..84d66220255 100644 --- a/src/vs/workbench/api/common/extHostMcp.ts +++ b/src/vs/workbench/api/common/extHostMcp.ts @@ -89,9 +89,9 @@ export class ExtHostMcpService extends Disposable implements IExtHostMpcService public registerMcpConfigurationProvider(extension: IExtensionDescription, id: string, provider: vscode.McpServerDefinitionProvider): IDisposable { const store = new DisposableStore(); - const metadata = extension.contributes?.modelContextServerCollections?.find(m => m.id === id); + const metadata = extension.contributes?.mcpServerDefinitionProviders?.find(m => m.id === id); if (!metadata) { - throw new Error(`MCP configuration providers must be registered in the contributes.modelContextServerCollections array within your package.json, but "${id}" was not`); + throw new Error(`MCP configuration providers must be registered in the contributes.mcpServerDefinitionProviders array within your package.json, but "${id}" was not`); } const mcp: McpCollectionDefinition.FromExtHost = { @@ -132,10 +132,13 @@ export class ExtHostMcpService extends Disposable implements IExtHostMpcService this._proxy.$deleteMcpCollection(mcp.id); })); - if (provider.onDidChangeServerDefinitions) { - store.add(provider.onDidChangeServerDefinitions(update)); + if (provider.onDidChangeMcpServerDefinitions) { + store.add(provider.onDidChangeMcpServerDefinitions(update)); } // todo@connor4312: proposed API back-compat + if ((provider as any).onDidChangeServerDefinitions) { + store.add((provider as any).onDidChangeServerDefinitions(update)); + } if ((provider as any).onDidChange) { store.add((provider as any).onDidChange(update)); } diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts index 3d2f7817405..f4849da1c6e 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts @@ -14,7 +14,12 @@ export type { McpConfigurationServer, IMcpConfigurationStdio, IMcpConfiguration const mcpActivationEventPrefix = 'onMcpCollection:'; -export const mcpActivationEvent = (collectionId: string) => mcpActivationEventPrefix + collectionId; +/** + * note: `contributedCollectionId` is _not_ the collection ID. The collection + * ID is formed by passing the contributed ID through `extensionPrefixedIdentifier` + */ +export const mcpActivationEvent = (contributedCollectionId: string) => + mcpActivationEventPrefix + contributedCollectionId; export const enum DiscoverySource { ClaudeDesktop = 'claude-desktop', @@ -141,7 +146,7 @@ export const mcpServerSchema: IJSONSchema = { }; export const mcpContributionPoint: IExtensionPointDescriptor = { - extensionPoint: 'modelContextServerCollections', + extensionPoint: 'mcpServerDefinitionProviders', activationEventsGenerator(contribs, result) { for (const contrib of contribs) { if (contrib.id) { diff --git a/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts b/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts index 9f36dd5aca1..11d6e1873e0 100644 --- a/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts @@ -8,7 +8,7 @@ declare module 'vscode' { /** * McpStdioServerDefinition represents an MCP server available by running - * a local process and listening to its stdin and stdout streams. The process + * a local process and operating on its stdin and stdout streams. The process * will be spawned as a child process of the extension host and by default * will not run in a shell environment. */ @@ -28,6 +28,7 @@ declare module 'vscode' { * `process.execPath` to use the editor's version of Node.js to run the script. */ command: string; + /** * Additional command-line arguments passed to the server. */ @@ -36,7 +37,7 @@ declare module 'vscode' { /** * Optional additional environment information for the server. Variables * in this environment will overwrite or remove (if null) the default - * environment variables. + * environment variables of the editor's extension host. */ env: Record; @@ -91,21 +92,22 @@ declare module 'vscode' { constructor(label: string, uri: Uri, headers?: Record, version?: string); } + /** + * Definitions that describe different types of Model Context Protocol servers, + * which can be returned from the {@link McpServerDefinitionProvider}. + */ export type McpServerDefinition = McpStdioServerDefinition | McpHttpServerDefinition; /** - * A type that can provide server configurations. This may only be used in - * conjunction with `contributes.modelContextServerCollections` in the - * extension's package.json. - * - * To allow the editor to cache available servers, extensions should register - * this before `activate()` resolves. + * A type that can provide Model Context Protocol server definitions. This + * should be registered using {@link lm.registerMcpServerDefinitionProvider} + * during extension activation. */ export interface McpServerDefinitionProvider { /** * Optional event fired to signal that the set of available servers has changed. */ - onDidChangeServerDefinitions?: Event; + onDidChangeMcpServerDefinitions?: Event; /** * Provides available MCP servers. The editor will call this method eagerly @@ -121,19 +123,45 @@ declare module 'vscode' { /** * This function will be called when the editor needs to start MCP server. * At this point, the extension may take any actions which may require user - * interaction, such as authentication. + * interaction, such as authentication. Any non-`readonly` property of the + * server may be modified, and the extension may return a new server. * - * The extension may return undefined on error to indicate that the server - * should not be started. + * The extension may return undefined to indicate that the server + * should not be started, or throw an error. If there is a pending tool + * call, the editor will cancel it and return an error message to the + * language model. * * @param server The MCP server to resolve * @param token A cancellation token. - * @returns The given, resolved server or thenable that resolves to such. + * @returns The resolved server or thenable that resolves to such. */ resolveMcpServerDefinition?(server: T, token: CancellationToken): ProviderResult; } namespace lm { + /** + * Registers a provider that publishes Model Context Protocol servers for the editor to + * consume. This allows MCP servers to be dynamically provided to the editor in + * addition to those the user creates in their configuration files. + * + * Before calling this method, extensions must register the `contributes.mcpServerDefinitionProviders` + * extension point with the corresponding {@link id}, for example: + * + * ```js + * "contributes": { + * "mcpServerDefinitionProviders": [ + * { + * "id": "cool-cloud-registry.mcp-servers", + * "label": "Cool Cloud Registry", + * } + * ] + * } + * ``` + * + * When a new McpServerDefinitionProvider is available, the editor will present a 'refresh' + * action to the user to discover new servers. To enable this flow, extensions should + * call `registerMcpServerDefinitionProvider` during activation. + */ export function registerMcpServerDefinitionProvider(id: string, provider: McpServerDefinitionProvider): Disposable; } } From d667ffe026e82d93ae21c800f8ace0ea1a941c2a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 6 May 2025 19:30:36 +0000 Subject: [PATCH 34/90] Git - revert some changes that are not needed (#248215) --- extensions/git/src/commands.ts | 12 ++++++------ extensions/git/src/staging.ts | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index fc38ccad68a..2395adb358f 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2010,12 +2010,12 @@ export class CommandCenter { this.logger.trace(`[CommandCenter][unstageSelectedRanges] selectedDiffs: ${JSON.stringify(selectedDiffs)}`); - if (modifiedUri.scheme === 'file') { - // Editor - this.logger.trace(`[CommandCenter][unstageSelectedRanges] changes: ${JSON.stringify(selectedDiffs)}`); - await this._unstageChanges(textEditor, selectedDiffs); - return; - } + // if (modifiedUri.scheme === 'file') { + // // Editor + // this.logger.trace(`[CommandCenter][unstageSelectedRanges] changes: ${JSON.stringify(selectedDiffs)}`); + // await this._unstageChanges(textEditor, selectedDiffs); + // return; + // } const selectedDiffsInverted = selectedDiffs.map(invertLineChange); this.logger.trace(`[CommandCenter][unstageSelectedRanges] selectedDiffsInverted: ${JSON.stringify(selectedDiffsInverted)}`); diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 32df8240916..14f10187f22 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -208,9 +208,10 @@ export function compareLineChanges(a: LineChange, b: LineChange): number { } export function getIndexDiffInformation(textEditor: TextEditor): TextEditorDiffInformation | undefined { - // Diff Editor (Index) | Text Editor + // Diff Editor (Index) return textEditor.diffInformation?.find(diff => - diff.original && isGitUri(diff.original) && fromGitUri(diff.original).ref === 'HEAD'); + diff.original && isGitUri(diff.original) && fromGitUri(diff.original).ref === 'HEAD' && + diff.modified && isGitUri(diff.modified) && fromGitUri(diff.modified).ref === ''); } export function getWorkingTreeDiffInformation(textEditor: TextEditor): TextEditorDiffInformation | undefined { From 85b3e45d77c8cca82c581aecae2257ff96ba9da0 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 6 May 2025 12:50:19 -0700 Subject: [PATCH 35/90] chat: fix standalone chat window moves focus when tool call prompts occur (#248243) Fixes #248156 --- .../chatContentParts/chatConfirmationContentPart.ts | 2 +- .../browser/chatContentParts/chatConfirmationWidget.ts | 10 ++++++---- .../chat/browser/chatContentParts/chatContentParts.ts | 1 + .../browser/chatContentParts/chatToolInvocationPart.ts | 9 ++++++--- .../workbench/contrib/chat/browser/chatListRenderer.ts | 2 ++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts index 1019e32cfc6..d8131034ce1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts @@ -39,7 +39,7 @@ export class ChatConfirmationContentPart extends Disposable implements IChatCont { label: localize('accept', "Accept"), data: confirmation.data }, { label: localize('dismiss', "Dismiss"), data: confirmation.data, isSecondary: true }, ]; - const confirmationWidget = this._register(this.instantiationService.createInstance(ChatConfirmationWidget, confirmation.title, undefined, confirmation.message, buttons)); + const confirmationWidget = this._register(this.instantiationService.createInstance(ChatConfirmationWidget, confirmation.title, undefined, confirmation.message, buttons, context.container)); confirmationWidget.setShowButtons(!confirmation.isUsed); this._register(confirmationWidget.onDidChangeHeight(() => this._onDidChangeHeight.fire())); diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts index 2298b1dd4f4..f23f60ebe20 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts @@ -167,11 +167,11 @@ abstract class BaseChatConfirmationWidget extends Disposable { }); } - protected renderMessage(element: HTMLElement): void { + protected renderMessage(element: HTMLElement, listContainer: HTMLElement): void { this.messageElement.append(element); if (this._configurationService.getValue('chat.focusWindowOnConfirmation')) { - const targetWindow = dom.getWindow(element); + const targetWindow = dom.getWindow(listContainer); if (!targetWindow.document.hasFocus()) { this._hostService.focus(targetWindow, { force: true /* Application may not be active */ }); } @@ -185,6 +185,7 @@ export class ChatConfirmationWidget extends BaseChatConfirmationWidget { subtitle: string | IMarkdownString | undefined, private readonly message: string | IMarkdownString, buttons: IChatConfirmationButton[], + container: HTMLElement, @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @@ -196,7 +197,7 @@ export class ChatConfirmationWidget extends BaseChatConfirmationWidget { typeof this.message === 'string' ? new MarkdownString(this.message) : this.message, { asyncRenderCallback: () => this._onDidChangeHeight.fire() } )); - this.renderMessage(renderedMessage.element); + this.renderMessage(renderedMessage.element, container); } } @@ -206,12 +207,13 @@ export class ChatCustomConfirmationWidget extends BaseChatConfirmationWidget { subtitle: string | IMarkdownString | undefined, messageElement: HTMLElement, buttons: IChatConfirmationButton[], + container: HTMLElement, @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IHostService hostService: IHostService, ) { super(title, subtitle, buttons, instantiationService, contextMenuService, configurationService, hostService); - this.renderMessage(messageElement); + this.renderMessage(messageElement, container); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts index c4d1f90c576..572e189b088 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts @@ -32,6 +32,7 @@ export interface IChatContentPart extends IDisposable { export interface IChatContentPartRenderContext { element: ChatTreeItem; + container: HTMLElement; content: ReadonlyArray; contentIndex: number; preceedingContentParts: ReadonlyArray; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts index dca560d629c..50ffbfe8dae 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts @@ -220,7 +220,8 @@ class ChatToolInvocationSubPart extends Disposable { title, toolInvocation.originMessage, message, - buttons + buttons, + this.context.container, )); } else { const chatMarkdownContent: IChatMarkdownContent = { @@ -356,7 +357,8 @@ class ChatToolInvocationSubPart extends Disposable { title, toolInvocation.originMessage, elements.root, - buttons + buttons, + this.context.container, )); } @@ -473,7 +475,8 @@ class ChatToolInvocationSubPart extends Disposable { title, undefined, element, - buttons + buttons, + this.context.container, )); ChatContextKeys.Editing.hasToolConfirmation.bindTo(this.contextKeyService).set(true); diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index b966a950be8..1d11f67b0ab 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -567,6 +567,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer Date: Tue, 6 May 2025 21:07:28 +0000 Subject: [PATCH 36/90] SCM - Fix quick diff navigation commands (#248242) * Fix Show Next Change/Go to Next Change commands * Fix Show Previous Change command --- .../contrib/scm/browser/quickDiffModel.ts | 25 ++++++++++++++++--- .../contrib/scm/browser/quickDiffWidget.ts | 2 +- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index 2a88b551251..ebdfee28049 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -409,11 +409,19 @@ export class QuickDiffModel extends Disposable { if (!inclusive) { // Next visible change - const nextChange = this.changes + let nextChangeIndex = this.changes .findIndex(change => visibleQuickDiffLabels.includes(change.label) && change.change.modifiedStartLineNumber > lineNumber); - return nextChange !== -1 ? nextChange : 0; + if (nextChangeIndex !== -1) { + return nextChangeIndex; + } + + // First visible change + nextChangeIndex = this.changes + .findIndex(change => visibleQuickDiffLabels.includes(change.label)); + + return nextChangeIndex !== -1 ? nextChangeIndex : 0; } const primaryQuickDiffId = this.quickDiffs @@ -428,12 +436,21 @@ export class QuickDiffModel extends Disposable { return primaryInclusiveChangeIndex; } - const inclusiveChangeIndex = this.changes + // Next visible change + let nextChangeIndex = this.changes .findIndex(change => visibleQuickDiffLabels.includes(change.label) && change.change.modifiedStartLineNumber <= lineNumber && getModifiedEndLineNumber(change.change) >= lineNumber); - return inclusiveChangeIndex !== -1 ? inclusiveChangeIndex : 0; + if (nextChangeIndex !== -1) { + return nextChangeIndex; + } + + // First visible change + nextChangeIndex = this.changes + .findIndex(change => visibleQuickDiffLabels.includes(change.label)); + + return nextChangeIndex !== -1 ? nextChangeIndex : 0; } findPreviousClosestChange(lineNumber: number, inclusive = true, provider?: string): number { diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index 1c6bebfe09b..646f00f6ece 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -562,7 +562,7 @@ export class QuickDiffEditorController extends Disposable implements IEditorCont } let index: number; - if (this.editor.hasModel() && (typeof lineNumber === 'number')) { + if (this.editor.hasModel() && (typeof lineNumber === 'number' || !this.widget.provider)) { index = this.model.findPreviousClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber, true, this.widget.provider); } else { const providerChanges: number[] = this.model.quickDiffChanges.get(this.widget.provider) ?? this.model.quickDiffChanges.values().next().value!; From fa00def55343cc56736a8242cd1706443c53d6d8 Mon Sep 17 00:00:00 2001 From: Peter Elmers Date: Tue, 6 May 2025 23:46:37 +0200 Subject: [PATCH 37/90] Fix cancellation logic in Picker onDidChangeValue handler (fixes #247945) (#247946) fix the slow search --- src/vs/platform/quickinput/browser/pickerQuickAccess.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts index e2eb9fd00c2..d200f15a66e 100644 --- a/src/vs/platform/quickinput/browser/pickerQuickAccess.ts +++ b/src/vs/platform/quickinput/browser/pickerQuickAccess.ts @@ -146,12 +146,13 @@ export abstract class PickerQuickAccessProvider { - const picksDisposables = picksDisposable.value = new DisposableStore(); - // Cancel any previous ask for picks and busy picksCts?.dispose(true); picker.busy = false; + // Setting the .value will call dispose() on the previous value, so we need to do this AFTER cancelling with dispose(true). + const picksDisposables = picksDisposable.value = new DisposableStore(); + // Create new cancellation source for this run picksCts = picksDisposables.add(new CancellationTokenSource(token)); From ff1b63d1f48e46219e16ecec0099e2fcf277a162 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 6 May 2025 14:48:32 -0700 Subject: [PATCH 38/90] Rename file Too many `dnd` files --- src/vs/editor/browser/{dnd.ts => dataTransfer.ts} | 2 +- .../contrib/dropOrPasteInto/browser/copyPasteController.ts | 2 +- .../contrib/dropOrPasteInto/browser/dropIntoEditorController.ts | 2 +- src/vs/workbench/browser/parts/views/treeView.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/vs/editor/browser/{dnd.ts => dataTransfer.ts} (97%) diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dataTransfer.ts similarity index 97% rename from src/vs/editor/browser/dnd.ts rename to src/vs/editor/browser/dataTransfer.ts index ef63da81a74..014c3cdac21 100644 --- a/src/vs/editor/browser/dnd.ts +++ b/src/vs/editor/browser/dataTransfer.ts @@ -10,7 +10,7 @@ import { URI } from '../../base/common/uri.js'; import { CodeDataTransfers, getPathForFile } from '../../platform/dnd/browser/dnd.js'; -export function toVSDataTransfer(dataTransfer: DataTransfer) { +export function toVSDataTransfer(dataTransfer: DataTransfer): VSDataTransfer { const vsDataTransfer = new VSDataTransfer(); for (const item of dataTransfer.items) { const type = item.type; diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts index b2b37a93a10..f995539bb62 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -25,7 +25,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js'; import { ClipboardEventUtils } from '../../../browser/controller/editContext/clipboardUtils.js'; -import { toExternalVSDataTransfer, toVSDataTransfer } from '../../../browser/dnd.js'; +import { toExternalVSDataTransfer, toVSDataTransfer } from '../../../browser/dataTransfer.js'; import { ICodeEditor, PastePayload } from '../../../browser/editorBrowser.js'; import { IBulkEditService } from '../../../browser/services/bulkEditService.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts index 54a5d6d4fe4..2a3cd0bef7d 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts @@ -16,7 +16,7 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { LocalSelectionTransfer } from '../../../../platform/dnd/browser/dnd.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { toExternalVSDataTransfer } from '../../../browser/dnd.js'; +import { toExternalVSDataTransfer } from '../../../browser/dataTransfer.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { IPosition } from '../../../common/core/position.js'; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index bc660c6f7a1..76b0e1c8810 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -63,7 +63,7 @@ import { IActivityService, NumberBadge } from '../../../services/activity/common import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IHoverService, WorkbenchHoverDelegate } from '../../../../platform/hover/browser/hover.js'; import { CodeDataTransfers, LocalSelectionTransfer } from '../../../../platform/dnd/browser/dnd.js'; -import { toExternalVSDataTransfer } from '../../../../editor/browser/dnd.js'; +import { toExternalVSDataTransfer } from '../../../../editor/browser/dataTransfer.js'; import { CheckboxStateHandler, TreeItemCheckbox } from './checkbox.js'; import { setTimeout0 } from '../../../../base/common/platform.js'; import { AriaRole } from '../../../../base/browser/ui/aria/aria.js'; From 5ba5d94744e50d98448ca97bf4e97bdc996ca448 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Wed, 30 Apr 2025 21:17:39 -0700 Subject: [PATCH 39/90] add linting config for the prompt files source code --- eslint.config.js | 94 ++++++++++++++++++- src/tsconfig.strict.json | 19 ++++ src/vs/editor/common/codecs/baseToken.ts | 1 + src/vs/editor/common/codecs/compositeToken.ts | 2 +- .../codecs/markdownCodec/markdownDecoder.ts | 4 +- .../markdownExtensionsDecoder.ts | 2 +- .../codecs/simpleCodec/simpleDecoder.ts | 4 +- src/vs/platform/prompts/common/config.ts | 6 +- .../promptSyntax/service/promptsService.ts | 21 ++--- 9 files changed, 132 insertions(+), 21 deletions(-) create mode 100644 src/tsconfig.strict.json diff --git a/eslint.config.js b/eslint.config.js index 91e3a6c31d6..0a69ba15dac 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1434,5 +1434,97 @@ export default tseslint.config( '@typescript-eslint/prefer-optional-chain': 'warn', '@typescript-eslint/prefer-readonly': 'warn', } - } + }, + // TODO: @lego + { + extends: [ + ...tseslint.configs.recommendedTypeChecked, + // TODO: @lego + // ...tseslint.configs.strictTypeChecked, + ], + files: [ + 'src/vs/platform/prompts/**/*.ts', + 'src/vs/editor/common/codecs/**/*.ts', + 'src/vs/workbench/contrib/chat/common/promptSyntax/**/*.ts', + ], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + // TODO: @lego + project: [ + 'src/tsconfig.strict.json', + ], + } + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + '@stylistic/ts': stylisticTs, + }, + rules: { + '@typescript-eslint/prefer-readonly': 'warn', + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/consistent-type-assertions': ['error', { 'assertionStyle': 'never' }], + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { + allowDirectConstAssertionInArrowFunctions: false, + }, + ], + '@typescript-eslint/explicit-member-accessibility': [ + 'error', + { + accessibility: 'explicit', + ignoredMethodNames: ['constructor'], + } + ], + '@typescript-eslint/explicit-module-boundary-types': 'error', + 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/ban-ts-comment': 'error', + 'default-param-last': 'off', '@typescript-eslint/default-param-last': 'error', + 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-array-delete': 'error', + '@typescript-eslint/no-base-to-string': 'error', + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + '@typescript-eslint/no-confusing-void-expression': 'error', + '@typescript-eslint/no-duplicate-enum-values': 'error', + '@typescript-eslint/no-dynamic-delete': 'error', + 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ + 'error', + { + 'allow': [ + 'private-constructors' + ] + } + ], + '@typescript-eslint/no-empty-object-type': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extra-non-null-assertion': 'error', + '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-for-in-array': 'error', + 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', + '@typescript-eslint/no-misused-new': 'warn', + '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/no-mixed-enums': 'error', + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-redundant-type-constituents': 'error', + '@typescript-eslint/naming-convention': [ + 'warn', + { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, + { 'selector': 'variable', 'filter': '^I.+Service$', 'format': ['PascalCase'], 'prefix': ['I'] }, + { 'selector': 'enumMember', 'format': ['PascalCase'] }, + { 'selector': 'typeAlias', 'format': ['PascalCase'], 'prefix': ['T'] }, + { 'selector': 'interface', 'format': ['PascalCase'], 'prefix': ['I'] } + ], + 'comma-dangle': ['warn', 'only-multiline'], + // // TODO: @lego + // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', + // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations + '@typescript-eslint/restrict-template-expressions': 'off', + } + }, ); diff --git a/src/tsconfig.strict.json b/src/tsconfig.strict.json new file mode 100644 index 00000000000..c4b8970aca0 --- /dev/null +++ b/src/tsconfig.strict.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "allowUnusedLabels": false, + "allowUnreachableCode": false, + "alwaysStrict": true, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strict": true, + } +} diff --git a/src/vs/editor/common/codecs/baseToken.ts b/src/vs/editor/common/codecs/baseToken.ts index 558905cb64a..14919c9a44d 100644 --- a/src/vs/editor/common/codecs/baseToken.ts +++ b/src/vs/editor/common/codecs/baseToken.ts @@ -114,6 +114,7 @@ export abstract class BaseToken { firstToken.range.startLineNumber <= lastToken.range.startLineNumber, 'First token must start on previous or the same line as the last token.', ); + if ((firstToken !== lastToken) && (firstToken.range.startLineNumber === lastToken.range.startLineNumber)) { assert( firstToken.range.endColumn <= lastToken.range.startColumn, diff --git a/src/vs/editor/common/codecs/compositeToken.ts b/src/vs/editor/common/codecs/compositeToken.ts index a751865f323..0659b55a0d6 100644 --- a/src/vs/editor/common/codecs/compositeToken.ts +++ b/src/vs/editor/common/codecs/compositeToken.ts @@ -17,7 +17,7 @@ export abstract class CompositeToken< super(BaseToken.fullRange(childTokens)); } - public override get text() { + public override get text(): string { return BaseToken.render(this.childTokens); } diff --git a/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts index 7825ab6ac88..7dd32661181 100644 --- a/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts +++ b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts @@ -91,8 +91,8 @@ export class MarkdownDecoder extends BaseDecoder while (i < lineText.length) { // index is 0-based, but column numbers are 1-based const columnNumber = i + 1; + const character = lineText[i]; // check if the current character is a well-known token const tokenConstructor = WELL_KNOWN_TOKENS .find((wellKnownToken) => { - return wellKnownToken.symbol === lineText[i]; + return wellKnownToken.symbol === character; }); // if it is a well-known token, emit it and continue to the next one diff --git a/src/vs/platform/prompts/common/config.ts b/src/vs/platform/prompts/common/config.ts index 1b992112e08..f46d38bff3b 100644 --- a/src/vs/platform/prompts/common/config.ts +++ b/src/vs/platform/prompts/common/config.ts @@ -107,9 +107,9 @@ export namespace PromptsConfig { } // copy all the enabled paths to the result list - for (const [path, enabled] of Object.entries(value)) { + for (const [path, enabledValue] of Object.entries(value)) { // we already added the default source folder, so skip it - if ((enabled === false) || (path === defaultSourceFolder)) { + if ((enabledValue === false) || (path === defaultSourceFolder)) { continue; } @@ -132,7 +132,7 @@ export namespace PromptsConfig { * be clearly mapped to a boolean (e.g., `"true"`, `"TRUE"`, `"FaLSe"`, etc.), * `undefined` for rest of the values */ -export const asBoolean = (value: any): boolean | undefined => { +export const asBoolean = (value: unknown): boolean | undefined => { if (typeof value === 'boolean') { return value; } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index 8fc054c5d81..4c0c59e73f3 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -8,16 +8,16 @@ import { localize } from '../../../../../../nls.js'; import { PROMPT_LANGUAGE_ID } from '../constants.js'; import { flatten, forEach } from '../utils/treeUtils.js'; import { PromptParser } from '../parsers/promptParser.js'; -import { URI } from '../../../../../../base/common/uri.js'; -import { IPromptFileReference } from '../parsers/types.js'; import { match } from '../../../../../../base/common/glob.js'; import { pick } from '../../../../../../base/common/arrays.js'; +import { type URI } from '../../../../../../base/common/uri.js'; +import { type IPromptFileReference } from '../parsers/types.js'; import { assert } from '../../../../../../base/common/assert.js'; import { basename } from '../../../../../../base/common/path.js'; import { ResourceSet } from '../../../../../../base/common/map.js'; import { PromptFilesLocator } from '../utils/promptFilesLocator.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { type ITextModel } from '../../../../../../editor/common/model.js'; import { ObjectCache } from '../../../../../../base/common/objectCache.js'; import { ILogService } from '../../../../../../platform/log/common/log.js'; import { TextModelPromptParser } from '../parsers/textModelPromptParser.js'; @@ -27,13 +27,13 @@ import { logTime, TLogFunction } from '../../../../../../base/common/decorators/ import { PROMPT_FILE_EXTENSION } from '../../../../../../platform/prompts/common/constants.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IUserDataProfileService } from '../../../../../services/userDataProfile/common/userDataProfile.js'; -import { IChatPromptSlashCommand, TCombinedToolsMetadata, IMetadata, IPromptPath, IPromptsService, TPromptsStorage, TPromptsType } from './types.js'; +import type { IChatPromptSlashCommand, TCombinedToolsMetadata, IMetadata, IPromptPath, IPromptsService, TPromptsStorage, TPromptsType } from './types.js'; /** * Provides prompt services. */ export class PromptsService extends Disposable implements IPromptsService { - declare readonly _serviceBrand: undefined; + public declare readonly _serviceBrand: undefined; /** * Cache of text model content prompt parsers. @@ -160,9 +160,9 @@ export class PromptsService extends Disposable implements IPromptsService { if (result) { return result; } - const model = this.modelService.getModels().find(model => model.getLanguageId() === PROMPT_LANGUAGE_ID && getPromptCommandName(model.uri.path) === command); - if (model) { - return { uri: model.uri, storage: 'local', type: 'prompt' }; + const textModel = this.modelService.getModels().find(model => model.getLanguageId() === PROMPT_LANGUAGE_ID && getPromptCommandName(model.uri.path) === command); + if (textModel) { + return { uri: textModel.uri, storage: 'local', type: 'prompt' }; } return undefined; } @@ -300,7 +300,7 @@ export class PromptsService extends Disposable implements IPromptsService { return false; }, fileMetadata); - if ((chatMode) === ChatMode.Agent) { + if (chatMode === ChatMode.Agent) { return { tools: (hasTools) ? [...new Set(result)] @@ -410,8 +410,7 @@ const collectMetadata = ( }; }; - -export function getPromptCommandName(path: string) { +export function getPromptCommandName(path: string): string { const name = basename(path, PROMPT_FILE_EXTENSION); return name; } From 361cc979263c0adf1d031d6b1e83a884e515d72c Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 14:11:06 -0700 Subject: [PATCH 40/90] fix issues generated by the new linter config --- eslint.config.js | 83 +++---- .../parsers/frontMatterArray.ts | 47 ++-- .../linesCodec/tokens/carriageReturn.ts | 4 +- .../codecs/linesCodec/tokens/newLine.ts | 4 +- .../markdownCodec/parsers/markdownLink.ts | 8 +- .../common/codecs/simpleCodec/parserBase.ts | 2 +- .../simpleCodec/tokens/angleBrackets.ts | 4 +- .../common/codecs/simpleCodec/tokens/at.ts | 2 +- .../common/codecs/simpleCodec/tokens/colon.ts | 2 +- .../codecs/simpleCodec/tokens/curlyBraces.ts | 4 +- .../common/codecs/simpleCodec/tokens/dash.ts | 2 +- .../codecs/simpleCodec/tokens/dollarSign.ts | 2 +- .../codecs/simpleCodec/tokens/doubleQuote.ts | 2 +- .../common/codecs/simpleCodec/tokens/hash.ts | 2 +- .../codecs/simpleCodec/tokens/parentheses.ts | 4 +- .../common/codecs/simpleCodec/tokens/quote.ts | 2 +- .../common/codecs/simpleCodec/tokens/slash.ts | 2 +- .../common/codecs/simpleCodec/tokens/space.ts | 2 +- .../common/codecs/simpleCodec/tokens/tab.ts | 2 +- .../codecs/simpleCodec/tokens/verticalTab.ts | 2 +- .../prompts/test/common/utils/mock.test.ts | 228 ++++++++++++------ .../providers/promptPathAutocompletion.ts | 4 +- .../promptSyntax/parsers/basePromptParser.ts | 20 +- .../chat/common/promptSyntax/parsers/types.ts | 8 +- .../promptSyntax/promptFileReference.test.ts | 4 +- .../testUtils/expectedReference.ts | 4 +- 26 files changed, 276 insertions(+), 174 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 0a69ba15dac..92add0ccdb8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1437,11 +1437,12 @@ export default tseslint.config( }, // TODO: @lego { - extends: [ - ...tseslint.configs.recommendedTypeChecked, - // TODO: @lego - // ...tseslint.configs.strictTypeChecked, - ], + // TODO: @lego + // extends: [ + // ...tseslint.configs.recommendedTypeChecked, + // // TODO: @lego + // // ...tseslint.configs.strictTypeChecked, + // ], files: [ 'src/vs/platform/prompts/**/*.ts', 'src/vs/editor/common/codecs/**/*.ts', @@ -1477,41 +1478,41 @@ export default tseslint.config( ignoredMethodNames: ['constructor'], } ], - '@typescript-eslint/explicit-module-boundary-types': 'error', 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', '@typescript-eslint/ban-ts-comment': 'error', 'default-param-last': 'off', '@typescript-eslint/default-param-last': 'error', 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', - '@typescript-eslint/no-array-delete': 'error', - '@typescript-eslint/no-base-to-string': 'error', - '@typescript-eslint/no-confusing-non-null-assertion': 'error', - '@typescript-eslint/no-confusing-void-expression': 'error', - '@typescript-eslint/no-duplicate-enum-values': 'error', - '@typescript-eslint/no-dynamic-delete': 'error', - 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ - 'error', - { - 'allow': [ - 'private-constructors' - ] - } - ], - '@typescript-eslint/no-empty-object-type': 'error', - '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-extra-non-null-assertion': 'error', - '@typescript-eslint/no-extraneous-class': 'error', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/no-for-in-array': 'error', - 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', - '@typescript-eslint/no-invalid-void-type': 'error', - 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', - '@typescript-eslint/no-misused-new': 'warn', - '@typescript-eslint/no-misused-promises': 'error', - '@typescript-eslint/no-mixed-enums': 'error', - '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', - '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', - '@typescript-eslint/no-non-null-assertion': 'error', - '@typescript-eslint/no-redundant-type-constituents': 'error', + // '@typescript-eslint/explicit-module-boundary-types': 'error', + // '@typescript-eslint/no-array-delete': 'error', + // '@typescript-eslint/no-base-to-string': 'error', + // '@typescript-eslint/no-confusing-non-null-assertion': 'error', + // '@typescript-eslint/no-confusing-void-expression': 'error', + // '@typescript-eslint/no-duplicate-enum-values': 'error', + // '@typescript-eslint/no-dynamic-delete': 'error', + // 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ + // 'error', + // { + // 'allow': [ + // 'private-constructors' + // ] + // } + // ], + // '@typescript-eslint/no-empty-object-type': 'error', + // '@typescript-eslint/no-explicit-any': 'error', + // '@typescript-eslint/no-extra-non-null-assertion': 'error', + // '@typescript-eslint/no-extraneous-class': 'error', + // '@typescript-eslint/no-floating-promises': 'error', + // '@typescript-eslint/no-for-in-array': 'error', + // 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', + // '@typescript-eslint/no-invalid-void-type': 'error', + // 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', + // '@typescript-eslint/no-misused-new': 'warn', + // '@typescript-eslint/no-misused-promises': 'error', + // '@typescript-eslint/no-mixed-enums': 'error', + // '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + // '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + // '@typescript-eslint/no-non-null-assertion': 'error', + // '@typescript-eslint/no-redundant-type-constituents': 'error', '@typescript-eslint/naming-convention': [ 'warn', { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, @@ -1520,11 +1521,11 @@ export default tseslint.config( { 'selector': 'typeAlias', 'format': ['PascalCase'], 'prefix': ['T'] }, { 'selector': 'interface', 'format': ['PascalCase'], 'prefix': ['I'] } ], - 'comma-dangle': ['warn', 'only-multiline'], - // // TODO: @lego - // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', - // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations - '@typescript-eslint/restrict-template-expressions': 'off', + // 'comma-dangle': ['warn', 'only-multiline'], + // // // // TODO: @lego + // // // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', + // // // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations + // // '@typescript-eslint/restrict-template-expressions': 'off', } }, ); diff --git a/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts b/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts index 22b54f4517f..cf0d04ff8e4 100644 --- a/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts +++ b/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { VALID_SPACE_TOKENS } from '../constants.js'; -import { assert } from '../../../../../base/common/assert.js'; import { FrontMatterArray } from '../tokens/frontMatterArray.js'; import { assertDefined } from '../../../../../base/common/types.js'; import { FrontMatterValueToken } from '../tokens/frontMatterToken.js'; import { TSimpleDecoderToken } from '../../simpleCodec/simpleDecoder.js'; +import { assert, assertNever } from '../../../../../base/common/assert.js'; import { Comma, LeftBracket, RightBracket } from '../../simpleCodec/tokens/index.js'; import { PartialFrontMatterValue, VALID_VALUE_START_TOKENS } from './frontMatterValue.js'; import { assertNotConsumed, ParserBase, TAcceptTokenResult } from '../../simpleCodec/parserBase.js'; @@ -22,6 +22,13 @@ const VALID_DELIMITER_TOKENS = Object.freeze([ Comma, ]); +// readonly (Exclude<(typeof VALID_SPACE_TOKENS[number] | typeof Comma), typeof VALID_VALUE_START_TOKENS[number]>)[] + +/** + * TODO: @legomushroom + */ +type IsUnion = T extends U ? never : never; + /** * Responsible for parsing an array syntax (or "inline sequence" * in YAML terms), e.g. `[1, '2', true, 2.54]` @@ -43,22 +50,28 @@ export class PartialFrontMatterArray extends ParserBase, + '', + ); super([startToken]); } diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts index 63c43190a54..c1406b4f65b 100644 --- a/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts +++ b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts @@ -24,14 +24,14 @@ export class CarriageReturn extends SimpleToken<'\r'> { /** * The byte representation of the token. */ - public get byte() { + public get byte(): VSBuffer { return CarriageReturn.byte; } /** * Return text representation of the token. */ - public override get text() { + public override get text(): '\r' { return CarriageReturn.symbol; } diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts index c55719b405f..1548e90a1b2 100644 --- a/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts +++ b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts @@ -24,14 +24,14 @@ export class NewLine extends SimpleToken<'\n'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '\n' { return NewLine.symbol; } /** * The byte representation of the token. */ - public get byte() { + public get byte(): VSBuffer { return NewLine.byte; } diff --git a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts index 5ab1d06d5c9..bff174f097c 100644 --- a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts +++ b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { BaseToken } from '../../baseToken.js'; import { MarkdownLink } from '../tokens/markdownLink.js'; import { NewLine } from '../../linesCodec/tokens/newLine.js'; import { assert } from '../../../../../base/common/assert.js'; @@ -171,14 +172,11 @@ export class PartialMarkdownLink extends ParserBase { return token.text; }) - .join(''); + const caption = BaseToken.render(this.captionTokens); // create link reference string this.currentTokens.push(token); - const reference = this.currentTokens - .map((token) => { return token.text; }).join(''); + const reference = BaseToken.render(this.currentTokens); // return complete markdown link object return { diff --git a/src/vs/editor/common/codecs/simpleCodec/parserBase.ts b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts index c9422f682b5..afd3051309e 100644 --- a/src/vs/editor/common/codecs/simpleCodec/parserBase.ts +++ b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts @@ -115,7 +115,7 @@ export function assertNotConsumed>( _target: T, propertyKey: 'accept', descriptor: PropertyDescriptor, -) { +): PropertyDescriptor { // store the original method reference const originalMethod = descriptor.value; diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts index 07e67b585ea..d57acedc1c3 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts @@ -18,7 +18,7 @@ export class LeftAngleBracket extends SimpleToken<'<'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '<' { return LeftAngleBracket.symbol; } @@ -43,7 +43,7 @@ export class RightAngleBracket extends SimpleToken<'>'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '>' { return RightAngleBracket.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/at.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/at.ts index 3a6b1c9436d..25c44433afd 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/at.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/at.ts @@ -18,7 +18,7 @@ export class At extends SimpleToken<'@'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '@' { return At.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts index 714d212df64..a04b3e853df 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts @@ -18,7 +18,7 @@ export class Colon extends SimpleToken<':'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): ':' { return Colon.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/curlyBraces.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/curlyBraces.ts index 995daad3c40..9c13e501d27 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/curlyBraces.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/curlyBraces.ts @@ -18,7 +18,7 @@ export class LeftCurlyBrace extends SimpleToken<'{'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '{' { return LeftCurlyBrace.symbol; } @@ -43,7 +43,7 @@ export class RightCurlyBrace extends SimpleToken<'}'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '}' { return RightCurlyBrace.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts index caa9cd0997a..a77b441ad26 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts @@ -18,7 +18,7 @@ export class Dash extends SimpleToken<'-'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '-' { return Dash.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/dollarSign.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/dollarSign.ts index 569baae656e..5b81d0afda8 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/dollarSign.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/dollarSign.ts @@ -18,7 +18,7 @@ export class DollarSign extends SimpleToken<'$'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '$' { return DollarSign.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/doubleQuote.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/doubleQuote.ts index 00fa0fcff5b..ba041a03ed5 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/doubleQuote.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/doubleQuote.ts @@ -19,7 +19,7 @@ export class DoubleQuote extends SimpleToken<'"'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '"' { return DoubleQuote.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts index cc212135106..f499085859d 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts @@ -18,7 +18,7 @@ export class Hash extends SimpleToken<'#'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '#' { return Hash.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts index 0df099b7c4d..b7ef4f5bc2a 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts @@ -18,7 +18,7 @@ export class LeftParenthesis extends SimpleToken<'('> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '(' { return LeftParenthesis.symbol; } @@ -43,7 +43,7 @@ export class RightParenthesis extends SimpleToken<')'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): ')' { return RightParenthesis.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/quote.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/quote.ts index fbe44a96363..3c262f67b49 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/quote.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/quote.ts @@ -19,7 +19,7 @@ export class Quote extends SimpleToken<`'`> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '\'' { return Quote.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/slash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/slash.ts index ce8d03167b7..04d382bc89f 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/slash.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/slash.ts @@ -18,7 +18,7 @@ export class Slash extends SimpleToken<'/'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '/' { return Slash.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts index 15d3e95997f..4c31c241c43 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts @@ -17,7 +17,7 @@ import { SimpleToken } from './simpleToken.js'; /** * Return text representation of the token. */ - public override get text() { + public override get text(): ' ' { return Space.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts index 5b3cf8737b1..f99e4c3c730 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts @@ -18,7 +18,7 @@ export class Tab extends SimpleToken<'\t'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '\t' { return Tab.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts index dbddd697090..ea403a40bc8 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts @@ -18,7 +18,7 @@ export class VerticalTab extends SimpleToken<'\v'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '\v' { return VerticalTab.symbol; } diff --git a/src/vs/platform/prompts/test/common/utils/mock.test.ts b/src/vs/platform/prompts/test/common/utils/mock.test.ts index 955918d386e..9b8113a8e5f 100644 --- a/src/vs/platform/prompts/test/common/utils/mock.test.ts +++ b/src/vs/platform/prompts/test/common/utils/mock.test.ts @@ -13,91 +13,181 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes suite('mockService', () => { ensureNoDisposablesAreLeakedInTestSuite(); - test('• overrides properties and functions', () => { - interface ITestService { - readonly _serviceBrand: undefined; - prop1: string; - id: string; - readonly counter: number; - method1(arg: boolean): string; - testMethod2(arg: number): boolean; - } + suite('• mockObject', () => { + test('• overrides properties and functions', () => { + interface ITestObject { + foo: string; + bar: string; + readonly baz: number; + someMethod(arg: boolean): string; + anotherMethod(arg: number): boolean; + } - const mock = mockService({ - id: 'ciao!', - counter: 74, - testMethod2(arg: number): boolean { - return !isNaN(arg); - }, + const mock = mockObject({ + bar: 'oh hi!', + baz: 42, + anotherMethod(arg: number): boolean { + return isNaN(arg); + }, + }); + + typeCheck(mock); + + assert.strictEqual( + mock.bar, + 'oh hi!', + 'bar should be overriden', + ); + + assert.strictEqual( + mock.baz, + 42, + 'baz should be overriden', + ); + + assert( + !(mock.anotherMethod(randomInt(100))), + 'Must execute overriden method correctly 1.', + ); + + assert( + mock.anotherMethod(NaN), + 'Must execute overriden method correctly 2.', + ); + + assert.throws(() => { + // property is not overriden so must throw + // eslint-disable-next-line local/code-no-unused-expressions + mock.foo; + }); + + assert.throws(() => { + // function is not overriden so must throw + mock.someMethod(randomBoolean()); + }); }); - typeCheck(mock); + test('• immutability of the overrides object', () => { + interface ITestObject { + foo: string; + bar: string; + readonly baz: number; + someMethod(arg: boolean): string; + anotherMethod(arg: number): boolean; + } - assert.strictEqual( - mock.id, - 'ciao!', - 'id should be overridden', - ); + const overrides: Partial = { + baz: 4, + }; + const mock = mockObject(overrides); + typeCheck(mock); - assert.strictEqual( - mock.counter, - 74, - 'counter should be overridden', - ); + assert.strictEqual( + mock.baz, + 4, + 'baz should be overridden', + ); - assert( - mock.testMethod2(randomInt(100)), - 'Must execute overridden method correctly 1.', - ); + // overrides object must be immutable + assert.throws(() => { + overrides.foo = 'test'; + }); - assert( - !(mock.testMethod2(NaN)), - 'Must execute overridden method correctly 2.', - ); - - assert.throws(() => { - // property is not overridden so must throw - // eslint-disable-next-line local/code-no-unused-expressions - mock.prop1; - }); - - assert.throws(() => { - // function is not overridden so must throw - mock.method1(randomBoolean()); + assert.throws(() => { + overrides.someMethod = (arg: boolean): string => { + return `${arg}__${arg}`; + }; + }); }); }); - test('• immutability of the overrides object', () => { - interface ITestService { - readonly _serviceBrand: undefined; - foo: string; - bar: string; - readonly baz: boolean; - someMethod(arg: boolean): string; - anotherMethod(arg: number): boolean; - } + suite('• mockService', () => { + test('• overrides properties and functions', () => { + interface ITestService { + readonly _serviceBrand: undefined; + prop1: string; + id: string; + readonly counter: number; + method1(arg: boolean): string; + testMethod2(arg: number): boolean; + } - const overrides: Partial = { - baz: false, - }; - const mock = mockService(overrides); - typeCheck(mock); + const mock = mockService({ + id: 'ciao!', + counter: 74, + testMethod2(arg: number): boolean { + return !isNaN(arg); + }, + }); - assert.strictEqual( - mock.baz, - false, - 'baz should be overridden', - ); + typeCheck(mock); - // overrides object must be immutable - assert.throws(() => { - overrides.foo = 'test'; + assert.strictEqual( + mock.id, + 'ciao!', + 'id should be overridden', + ); + + assert.strictEqual( + mock.counter, + 74, + 'counter should be overridden', + ); + + assert( + mock.testMethod2(randomInt(100)), + 'Must execute overridden method correctly 1.', + ); + + assert( + !(mock.testMethod2(NaN)), + 'Must execute overridden method correctly 2.', + ); + + assert.throws(() => { + // property is not overridden so must throw + // eslint-disable-next-line local/code-no-unused-expressions + mock.prop1; + }); + + assert.throws(() => { + // function is not overridden so must throw + mock.method1(randomBoolean()); + }); }); - assert.throws(() => { - overrides.someMethod = (arg: boolean) => { - return `${arg}__${arg}`; + test('• immutability of the overrides object', () => { + interface ITestService { + readonly _serviceBrand: undefined; + foo: string; + bar: string; + readonly baz: boolean; + someMethod(arg: boolean): string; + anotherMethod(arg: number): boolean; + } + + const overrides: Partial = { + baz: false, }; + const mock = mockService(overrides); + typeCheck(mock); + + assert.strictEqual( + mock.baz, + false, + 'baz should be overridden', + ); + + // overrides object must be immutable + assert.throws(() => { + overrides.foo = 'test'; + }); + + assert.throws(() => { + overrides.someMethod = (arg: boolean): string => { + return `${arg}__${arg}`; + }; + }); }); }); }); diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptPathAutocompletion.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptPathAutocompletion.ts index a2c8b946b28..2bad7e5bbf7 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptPathAutocompletion.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptPathAutocompletion.ts @@ -23,7 +23,7 @@ import { Disposable } from '../../../../../../../../base/common/lifecycle.js'; import { CancellationError } from '../../../../../../../../base/common/errors.js'; import { PROMPT_AND_INSTRUCTIONS_LANGUAGE_SELECTOR } from '../../../constants.js'; import { Position } from '../../../../../../../../editor/common/core/position.js'; -import { IPromptFileReference, IPromptReference } from '../../../parsers/types.js'; +import { IPromptFileReference, TPromptReference } from '../../../parsers/types.js'; import { assert, assertNever } from '../../../../../../../../base/common/assert.js'; import { IFileService } from '../../../../../../../../platform/files/common/files.js'; import { CancellationToken } from '../../../../../../../../base/common/cancellation.js'; @@ -52,7 +52,7 @@ type TTriggerCharacter = ':' | '.' | '/'; * Finds a file reference that suites the provided `position`. */ const findFileReference = ( - references: readonly IPromptReference[], + references: readonly TPromptReference[], position: Position, ): IPromptFileReference | undefined => { for (const reference of references) { diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts index 31c6bb08494..d796c8761f6 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts @@ -24,7 +24,7 @@ import { assert, assertNever } from '../../../../../../base/common/assert.js'; import { basename, dirname } from '../../../../../../base/common/resources.js'; import { BaseToken } from '../../../../../../editor/common/codecs/baseToken.js'; import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js'; -import { IPromptMetadata, IPromptReference, IResolveError, ITopError } from './types.js'; +import { IPromptMetadata, TPromptReference, IResolveError, ITopError } from './types.js'; import { ObservableDisposable } from '../../../../../../base/common/observableDisposable.js'; import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js'; import { isPromptOrInstructionsFile } from '../../../../../../platform/prompts/common/constants.js'; @@ -85,7 +85,7 @@ export class BasePromptParser /** * List of file references in the current branch of the file reference tree. */ - private readonly _references: IPromptReference[] = []; + private readonly _references: TPromptReference[] = []; /** * Reference to the prompt header object that holds metadata associated @@ -513,7 +513,7 @@ export class BasePromptParser /** * Get a list of immediate child references of the prompt. */ - public get references(): readonly IPromptReference[] { + public get references(): readonly TPromptReference[] { return [...this._references]; } @@ -521,8 +521,8 @@ export class BasePromptParser * Get a list of all references of the prompt, including * all possible nested references its children may have. */ - public get allReferences(): readonly IPromptReference[] { - const result: IPromptReference[] = []; + public get allReferences(): readonly TPromptReference[] { + const result: TPromptReference[] = []; for (const reference of this.references) { result.push(reference); @@ -538,7 +538,7 @@ export class BasePromptParser /** * Get list of all valid references. */ - public get allValidReferences(): readonly IPromptReference[] { + public get allValidReferences(): readonly TPromptReference[] { return this.allReferences // filter out unresolved references .filter((reference) => { @@ -775,7 +775,7 @@ export class BasePromptParser * contents. For instance the file variable(`#file:/path/to/file.md`) or * a markdown link(`[#file:file.md](/path/to/file.md)`). */ -export class PromptReference extends ObservableDisposable implements IPromptReference { +export class PromptReference extends ObservableDisposable implements TPromptReference { /** * Instance of underlying prompt parser object. */ @@ -910,11 +910,11 @@ export class PromptReference extends ObservableDisposable implements IPromptRefe return this.parser.allErrors; } - public get references(): readonly IPromptReference[] { + public get references(): readonly TPromptReference[] { return this.parser.references; } - public get allReferences(): readonly IPromptReference[] { + public get allReferences(): readonly TPromptReference[] { return this.parser.allReferences; } @@ -926,7 +926,7 @@ export class PromptReference extends ObservableDisposable implements IPromptRefe return this.parser.allToolsMetadata; } - public get allValidReferences(): readonly IPromptReference[] { + public get allValidReferences(): readonly TPromptReference[] { return this.parser.allValidReferences; } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.ts index 8da3a8abaa9..42ba2d7f626 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.ts @@ -158,13 +158,13 @@ interface IPromptReferenceBase extends IDisposable { /** * Direct references of the current reference. */ - readonly references: readonly IPromptReference[]; + readonly references: readonly TPromptReference[]; /** * All references that the current reference may have, * including all possible nested child references. */ - readonly allReferences: readonly IPromptReference[]; + readonly allReferences: readonly TPromptReference[]; /** * All *valid* references that the current reference may have, @@ -174,7 +174,7 @@ interface IPromptReferenceBase extends IDisposable { * without creating a circular reference loop or having any other * issues that would make the reference resolve logic to fail. */ - readonly allValidReferences: readonly IPromptReference[]; + readonly allValidReferences: readonly TPromptReference[]; /** * Entire associated `tools` metadata for this reference and @@ -221,4 +221,4 @@ export interface IPromptFileReference extends IPromptReferenceBase { /** * List of all known prompt reference types. */ -export type IPromptReference = IPromptFileReference; +export type TPromptReference = IPromptFileReference; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts index f4fd4556e9a..c0422260939 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts @@ -12,7 +12,7 @@ import { assertDefined } from '../../../../../../base/common/types.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { IMockFolder, MockFilesystem } from './testUtils/mockFilesystem.js'; import { IFileService } from '../../../../../../platform/files/common/files.js'; -import { IPromptReference } from '../../../common/promptSyntax/parsers/types.js'; +import { TPromptReference } from '../../../common/promptSyntax/parsers/types.js'; import { IModelService } from '../../../../../../editor/common/services/model.js'; import { FileService } from '../../../../../../platform/files/common/fileService.js'; import { NullPolicyService } from '../../../../../../platform/policy/common/policy.js'; @@ -114,7 +114,7 @@ class TestPromptFileReference extends Disposable { await rootReference.allSettled(); // resolve the root file reference including all nested references - const resolvedReferences: readonly (IPromptReference | undefined)[] = rootReference.allReferences; + const resolvedReferences: readonly (TPromptReference | undefined)[] = rootReference.allReferences; for (let i = 0; i < this.expectedReferences.length; i++) { const expectedReference = this.expectedReferences[i]; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts index 2c91a7f730d..2ae0eaa36f4 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts @@ -8,7 +8,7 @@ import { URI } from '../../../../../../../base/common/uri.js'; import { Range } from '../../../../../../../editor/common/core/range.js'; import { assertDefined } from '../../../../../../../base/common/types.js'; import { ResolveError } from '../../../../common/promptFileReferenceErrors.js'; -import { IPromptReference } from '../../../../common/promptSyntax/parsers/types.js'; +import { TPromptReference } from '../../../../common/promptSyntax/parsers/types.js'; import { TErrorCondition } from '../../../../common/promptSyntax/parsers/basePromptParser.js'; /** @@ -63,7 +63,7 @@ export class ExpectedReference { /** * Validate that the provided reference is equal to this object. */ - public validateEqual(other: IPromptReference) { + public validateEqual(other: TPromptReference) { const { uri, text, path, childrenOrError = [] } = this.options; const errorPrefix = `[${uri}] `; From 68e20bd3d26ca3e57d80b1fa7a7ba0150e5ade3a Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 16:01:13 -0700 Subject: [PATCH 41/90] fix more linter errors --- .../codecs/simpleCodec/tokens/brackets.ts | 4 +- .../simpleCodec/tokens/exclamationMark.ts | 2 +- .../codecs/simpleCodec/tokens/formFeed.ts | 2 +- .../prompts/test/common/utils/mock.ts | 50 ++++++++++++++++++- .../codecs/tokens/fileReference.ts | 2 +- .../filePromptContentsProvider.ts | 4 +- .../textModelContentsProvider.ts | 4 +- .../promptSyntax/contributions/index.ts | 13 ++--- .../decorations/frontMatterDecoration.ts | 4 +- .../frontMatterMarkerDecoration.ts | 4 +- .../utils/reactiveDecorationBase.ts | 4 +- .../promptDecorationsProvider.ts | 6 +-- .../promptHeaderDiagnosticsProvider.ts | 6 +-- .../promptLinkDiagnosticsProvider.ts | 8 +-- .../providers/providerInstanceManagerBase.ts | 7 ++- .../promptSyntax/parsers/basePromptParser.ts | 12 ++--- .../promptSyntax/parsers/filePromptParser.ts | 4 +- .../promptSyntax/parsers/promptParser.ts | 4 +- .../parsers/textModelPromptParser.ts | 4 +- .../common/promptSyntax/parsers/topError.ts | 2 +- .../common/promptSyntax/utils/treeUtils.ts | 4 +- 21 files changed, 99 insertions(+), 51 deletions(-) diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts index 9137fc586b3..491cb2a588c 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts @@ -18,7 +18,7 @@ export class LeftBracket extends SimpleToken<'['> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '[' { return LeftBracket.symbol; } @@ -43,7 +43,7 @@ export class RightBracket extends SimpleToken<']'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): ']' { return RightBracket.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts index c019bdd256d..32675fdf8a3 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts @@ -18,7 +18,7 @@ export class ExclamationMark extends SimpleToken<'!'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '!' { return ExclamationMark.symbol; } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts index 699574ab46c..df5b8b0d446 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts @@ -18,7 +18,7 @@ export class FormFeed extends SimpleToken<'\f'> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): '\f' { return FormFeed.symbol; } diff --git a/src/vs/platform/prompts/test/common/utils/mock.ts b/src/vs/platform/prompts/test/common/utils/mock.ts index 9b9a088be78..c34231bd5e5 100644 --- a/src/vs/platform/prompts/test/common/utils/mock.ts +++ b/src/vs/platform/prompts/test/common/utils/mock.ts @@ -3,7 +3,55 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { mockObject } from '../../../../../base/test/common/testUtils.js'; +import { assertOneOf } from '../../../../../base/common/types.js'; + +/** + * Mocks an `TObject` with the provided `overrides`. + * + * If you need to mock an `Service`, please use {@link mockService} + * instead which provides better type safety guarantees for the case. + * + * @throws Reading non-overridden property or function + * on `TObject` throws an error. + */ +export function mockObject( + overrides: Partial, +): TObject { + // ensure that the overrides object cannot be modified afterward + overrides = Object.freeze(overrides); + + const keys: (keyof Partial)[] = []; + for (const key in overrides) { + if (Object.hasOwn(overrides, key)) { + keys.push(key); + } + } + + const service: object = new Proxy( + {}, + { + get: ( + _target: TObject, + key: string | number | Symbol, + ): TObject[T] => { + + assertOneOf( + key, + keys, + `The '${key}' is not mocked.`, + ); + + // TODO: @legomushroom - add type assertion comment + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return overrides[key as T] as TObject[T]; + }, + }); + + // note! it's ok to `as TObject` here, because of the runtime checks + // in the `Proxy` getter + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return service as TObject; +} /** * Type for any service. diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts index 50fc3489823..78c4f7cb6ea 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts @@ -28,7 +28,7 @@ export class FileReference extends PromptVariableWithData { * Create a {@link FileReference} from a {@link PromptVariableWithData} instance. * @throws if variable name is not equal to {@link VARIABLE_NAME}. */ - public static from(variable: PromptVariableWithData) { + public static from(variable: PromptVariableWithData): FileReference { assert( variable.name === VARIABLE_NAME, `Variable name must be '${VARIABLE_NAME}', got '${variable.name}'.`, diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts index 723bf10ea3f..27d9404edef 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts @@ -46,7 +46,7 @@ export class FilePromptContentProvider extends PromptContentsProviderBase = {}, + options: Partial, @IFileService private readonly fileService: IFileService, @IModelService private readonly modelService: IModelService, @ILanguageService private readonly languageService: ILanguageService, @@ -152,7 +152,7 @@ export class FilePromptContentProvider extends PromptContentsProviderBase = {}, + options: Partial, @IInstantiationService private readonly initService: IInstantiationService, @ILogService private readonly logService: ILogService, ) { @@ -149,7 +149,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase { +export const registerPromptFileContributions = (): void => { registerContributions(LANGUAGE_FEATURE_CONTRIBUTIONS); - registerContribution(ConfigMigration); }; @@ -26,9 +25,7 @@ export type TContribution = new (...args: any[]) => IWorkbenchContribution; /** * Register a specific workbench contribution. */ -const registerContribution = ( - contribution: TContribution, -) => { +const registerContribution = (contribution: TContribution): void => { Registry.as(Extensions.Workbench) .registerWorkbenchContribution(contribution, LifecyclePhase.Eventually); }; @@ -36,9 +33,7 @@ const registerContribution = ( /** * Register a specific workbench contribution. */ -const registerContributions = ( - contributions: readonly TContribution[], -) => { +const registerContributions = (contributions: readonly TContribution[]): void => { contributions - .map(registerContribution); + .forEach(registerContribution); }; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterDecoration.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterDecoration.ts index b2c0602dff4..e8be111d262 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterDecoration.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterDecoration.ts @@ -8,7 +8,7 @@ import { localize } from '../../../../../../../../../../nls.js'; import { FrontMatterMarkerDecoration } from './frontMatterMarkerDecoration.js'; import { Position } from '../../../../../../../../../../editor/common/core/position.js'; import { BaseToken } from '../../../../../../../../../../editor/common/codecs/baseToken.js'; -import { TAddAccessor, TDecorationStyles, ReactiveDecorationBase, asCssVariable } from './utils/index.js'; +import { TAddAccessor, TDecorationStyles, ReactiveDecorationBase, asCssVariable, IReactiveDecorationClassNames } from './utils/index.js'; import { contrastBorder, editorBackground } from '../../../../../../../../../../platform/theme/common/colorRegistry.js'; import { ColorIdentifier, darken, registerColor } from '../../../../../../../../../../platform/theme/common/colorUtils.js'; import { FrontMatterHeader } from '../../../../../../../../../../editor/common/codecs/markdownExtensionsCodec/tokens/frontMatterHeader.js'; @@ -92,7 +92,7 @@ export class FrontMatterDecoration extends ReactiveDecorationBase { return CssClassNames; } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterMarkerDecoration.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterMarkerDecoration.ts index 31ad42e50b4..9c382df0a33 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterMarkerDecoration.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/frontMatterMarkerDecoration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CssClassModifiers } from '../types.js'; -import { TDecorationStyles, ReactiveDecorationBase } from './utils/index.js'; +import { TDecorationStyles, ReactiveDecorationBase, IReactiveDecorationClassNames } from './utils/index.js'; import { FrontMatterMarker } from '../../../../../../../../../../editor/common/codecs/markdownExtensionsCodec/tokens/frontMatterMarker.js'; /** @@ -34,7 +34,7 @@ export class FrontMatterMarkerDecoration extends ReactiveDecorationBase { return CssClassNames; } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/utils/reactiveDecorationBase.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/utils/reactiveDecorationBase.ts index 2d07e97574b..ec78e9736c5 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/utils/reactiveDecorationBase.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/decorations/utils/reactiveDecorationBase.ts @@ -143,13 +143,13 @@ export abstract class ReactiveDecorationBase< return this; } - protected override get className() { + protected override get className(): TCssClassName { return (this.active) ? this.classNames.main : this.classNames.mainInactive; } - protected override get inlineClassName() { + protected override get inlineClassName(): TCssClassName { return (this.active) ? this.classNames.inline : this.classNames.inlineInactive; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts index 0b534a21a63..5fa9591504d 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts @@ -8,9 +8,9 @@ import { ProviderInstanceBase } from '../providerInstanceBase.js'; import { ITextModel } from '../../../../../../../../../editor/common/model.js'; import { FrontMatterDecoration } from './decorations/frontMatterDecoration.js'; import { toDisposable } from '../../../../../../../../../base/common/lifecycle.js'; -import { ProviderInstanceManagerBase } from '../providerInstanceManagerBase.js'; import { Position } from '../../../../../../../../../editor/common/core/position.js'; import { BaseToken } from '../../../../../../../../../editor/common/codecs/baseToken.js'; +import { ProviderInstanceManagerBase, TProviderInstance } from '../providerInstanceManagerBase.js'; import { registerThemingParticipant } from '../../../../../../../../../platform/theme/common/themeService.js'; import { FrontMatterHeader } from '../../../../../../../../../editor/common/codecs/markdownExtensionsCodec/tokens/frontMatterHeader.js'; import { DecorationBase, ReactiveDecorationBase, type TDecorationClass, type TChangedDecorator } from './decorations/utils/index.js'; @@ -178,7 +178,7 @@ export class PromptDecorator extends ProviderInstanceBase { /** * Returns a string representation of this object. */ - public override toString() { + public override toString(): string { return `text-model-prompt-decorator:${this.model.uri.path}`; } } @@ -198,7 +198,7 @@ registerThemingParticipant((_theme, collector) => { * Provider for prompt syntax decorators on text models. */ export class PromptDecorationsProviderInstanceManager extends ProviderInstanceManagerBase { - protected override get InstanceClass() { + protected override get InstanceClass(): TProviderInstance { return PromptDecorator; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts index a2caf8d250a..b7084e159b1 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts @@ -7,7 +7,7 @@ import { IPromptsService } from '../../../service/types.js'; import { ProviderInstanceBase } from './providerInstanceBase.js'; import { assertNever } from '../../../../../../../../base/common/assert.js'; import { ITextModel } from '../../../../../../../../editor/common/model.js'; -import { ProviderInstanceManagerBase } from './providerInstanceManagerBase.js'; +import { ProviderInstanceManagerBase, TProviderInstance } from './providerInstanceManagerBase.js'; import { TDiagnostic, PromptMetadataError, PromptMetadataWarning } from '../../../parsers/promptHeader/diagnostics.js'; import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../../../../../platform/markers/common/markers.js'; @@ -61,7 +61,7 @@ class PromptHeaderDiagnosticsProvider extends ProviderInstanceBase { /** * Returns a string representation of this object. */ - public override toString() { + public override toString(): string { return `prompt-link-diagnostics:${this.model.uri.path}`; } } @@ -100,7 +100,7 @@ const toMarker = ( * classes for each specific editor text model. */ export class PromptHeaderDiagnosticsInstanceManager extends ProviderInstanceManagerBase { - protected override get InstanceClass() { + protected override get InstanceClass(): TProviderInstance { return PromptHeaderDiagnosticsProvider; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts index 222a2c7808f..fe40077ceba 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts @@ -10,7 +10,7 @@ import { assert } from '../../../../../../../../base/common/assert.js'; import { NotPromptFile } from '../../../../promptFileReferenceErrors.js'; import { ITextModel } from '../../../../../../../../editor/common/model.js'; import { assertDefined } from '../../../../../../../../base/common/types.js'; -import { ProviderInstanceManagerBase } from './providerInstanceManagerBase.js'; +import { ProviderInstanceManagerBase, TProviderInstance } from './providerInstanceManagerBase.js'; import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../../../../../platform/markers/common/markers.js'; /** @@ -33,7 +33,7 @@ class PromptLinkDiagnosticsProvider extends ProviderInstanceBase { /** * Update diagnostic markers for the current editor. */ - protected override async onPromptSettled() { + protected override async onPromptSettled(): Promise { // ensure that parsing process is settled await this.parser.allSettled(); @@ -72,7 +72,7 @@ class PromptLinkDiagnosticsProvider extends ProviderInstanceBase { /** * Returns a string representation of this object. */ - public override toString() { + public override toString(): string { return `prompt-link-diagnostics:${this.model.uri.path}`; } } @@ -125,7 +125,7 @@ const toMarker = ( * classes for each specific editor text model. */ export class PromptLinkDiagnosticsInstanceManager extends ProviderInstanceManagerBase { - protected override get InstanceClass() { + protected override get InstanceClass(): TProviderInstance { return PromptLinkDiagnosticsProvider; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts index c766123e204..68670a2b11c 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts @@ -24,6 +24,11 @@ export interface IPromptFileEditor extends IEditor { readonly getModel: () => ITextModel; } +/** + * TODO: @legomushroom + */ +export type TProviderInstance = new (editor: ITextModel, ...args: any[]) => TInstance; + /** * A generic base class that manages creation and disposal of {@link TInstance} * objects for each specific editor object that is used for reusable prompt files. @@ -37,7 +42,7 @@ export abstract class ProviderInstanceManagerBase TInstance; + protected abstract get InstanceClass(): TProviderInstance; constructor( @IModelService modelService: IModelService, diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts index d796c8761f6..888f5f669ac 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts @@ -171,7 +171,7 @@ export class BasePromptParser * The promise is resolved when at least one parse result (a stream or * an error) has been received from the prompt contents provider. */ - private firstParseResult = new FirstParseResult(); + private readonly firstParseResult = new FirstParseResult(); /** * Returned promise is resolved when the parser process is settled. @@ -445,7 +445,7 @@ export class BasePromptParser /** * Dispose all currently held references. */ - private disposeReferences() { + private disposeReferences(): void { for (const reference of [...this._references]) { reference.dispose(); } @@ -753,7 +753,7 @@ export class BasePromptParser /** * @inheritdoc */ - public override dispose() { + public override dispose(): void { if (this.disposed) { return; } @@ -784,7 +784,7 @@ export class PromptReference extends ObservableDisposable implements TPromptRefe constructor( private readonly promptContentsProvider: IPromptContentsProvider, public readonly token: FileReference | MarkdownLink, - options: Partial = {}, + options: Partial, @IInstantiationService initService: IInstantiationService, ) { super(); @@ -945,7 +945,7 @@ export class PromptReference extends ObservableDisposable implements TPromptRefe /** * Returns a string representation of this object. */ - public override toString() { + public override toString(): string { return `prompt-reference/${this.type}:${this.subtype}/${this.token}`; } } @@ -978,7 +978,7 @@ class FirstParseResult extends DeferredPromise { /** * Complete the underlying promise. */ - public override complete() { + public override complete(): Promise { this._gotResult = true; return super.complete(void 0); } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts index ab4cd1fb7bc..b2078735461 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/filePromptParser.ts @@ -17,7 +17,7 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ export class FilePromptParser extends BasePromptParser { constructor( uri: URI, - options: Partial = {}, + options: Partial, @IInstantiationService initService: IInstantiationService, @IWorkspaceContextService workspaceService: IWorkspaceContextService, @ILogService logService: ILogService, @@ -31,7 +31,7 @@ export class FilePromptParser extends BasePromptParser { constructor( uri: URI, - options: Partial = {}, + options: Partial, @ILogService logService: ILogService, @IModelService modelService: IModelService, @IInstantiationService instaService: IInstantiationService, @@ -75,7 +75,7 @@ export class PromptParser extends BasePromptParser { /** * Returns a string representation of this object. */ - public override toString() { + public override toString(): string { const { sourceName } = this.contentsProvider; return `prompt-parser:${sourceName}:${this.uri.path}`; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts index 72f21fd0a5e..e2ebc518235 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts @@ -17,7 +17,7 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ export class TextModelPromptParser extends BasePromptParser { constructor( model: ITextModel, - options: Partial = {}, + options: Partial, @IInstantiationService initService: IInstantiationService, @IWorkspaceContextService workspaceService: IWorkspaceContextService, @ILogService logService: ILogService, @@ -36,7 +36,7 @@ export class TextModelPromptParser extends BasePromptParser, + options: Omit, ) { this.originalError = options.originalError; this.errorSubject = options.errorSubject; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts index bfbc36075e0..6c596c3ab7f 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts @@ -39,9 +39,9 @@ export const forEach = ( } for (const child of treeRoot.children ?? []) { - const shouldStop = forEach(callback, child); + const childShouldStop = forEach(callback, child); - if (shouldStop === true) { + if (childShouldStop === true) { return true; } } From baaabfff4aa86d69e561c5681517b7afe6b26d78 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 16:15:50 -0700 Subject: [PATCH 42/90] reduce number of linter rules, address all outstanding linter errors --- eslint.config.js | 102 +++++++++--------- .../parsers/frontMatterArray.ts | 34 +----- .../common/codecs/simpleCodec/tokens/comma.ts | 2 +- .../prompts/test/common/utils/mock.ts | 10 +- .../promptDecorationsProvider.ts | 4 +- .../promptHeaderDiagnosticsProvider.ts | 4 +- .../promptLinkDiagnosticsProvider.ts | 4 +- .../providers/providerInstanceManagerBase.ts | 6 +- 8 files changed, 68 insertions(+), 98 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 92add0ccdb8..46af66d6fd6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1471,61 +1471,61 @@ export default tseslint.config( allowDirectConstAssertionInArrowFunctions: false, }, ], - '@typescript-eslint/explicit-member-accessibility': [ - 'error', - { - accessibility: 'explicit', - ignoredMethodNames: ['constructor'], - } - ], - 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', - '@typescript-eslint/ban-ts-comment': 'error', - 'default-param-last': 'off', '@typescript-eslint/default-param-last': 'error', - 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', - // '@typescript-eslint/explicit-module-boundary-types': 'error', - // '@typescript-eslint/no-array-delete': 'error', - // '@typescript-eslint/no-base-to-string': 'error', - // '@typescript-eslint/no-confusing-non-null-assertion': 'error', - // '@typescript-eslint/no-confusing-void-expression': 'error', - // '@typescript-eslint/no-duplicate-enum-values': 'error', - // '@typescript-eslint/no-dynamic-delete': 'error', - // 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ + // '@typescript-eslint/explicit-member-accessibility': [ // 'error', // { - // 'allow': [ - // 'private-constructors' - // ] + // accessibility: 'explicit', + // ignoredMethodNames: ['constructor'], // } // ], - // '@typescript-eslint/no-empty-object-type': 'error', - // '@typescript-eslint/no-explicit-any': 'error', - // '@typescript-eslint/no-extra-non-null-assertion': 'error', - // '@typescript-eslint/no-extraneous-class': 'error', - // '@typescript-eslint/no-floating-promises': 'error', - // '@typescript-eslint/no-for-in-array': 'error', - // 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', - // '@typescript-eslint/no-invalid-void-type': 'error', - // 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', - // '@typescript-eslint/no-misused-new': 'warn', - // '@typescript-eslint/no-misused-promises': 'error', - // '@typescript-eslint/no-mixed-enums': 'error', - // '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', - // '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', - // '@typescript-eslint/no-non-null-assertion': 'error', - // '@typescript-eslint/no-redundant-type-constituents': 'error', - '@typescript-eslint/naming-convention': [ - 'warn', - { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, - { 'selector': 'variable', 'filter': '^I.+Service$', 'format': ['PascalCase'], 'prefix': ['I'] }, - { 'selector': 'enumMember', 'format': ['PascalCase'] }, - { 'selector': 'typeAlias', 'format': ['PascalCase'], 'prefix': ['T'] }, - { 'selector': 'interface', 'format': ['PascalCase'], 'prefix': ['I'] } - ], - // 'comma-dangle': ['warn', 'only-multiline'], - // // // // TODO: @lego - // // // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', - // // // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations - // // '@typescript-eslint/restrict-template-expressions': 'off', + // 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', + // '@typescript-eslint/ban-ts-comment': 'error', + // 'default-param-last': 'off', '@typescript-eslint/default-param-last': 'error', + // 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', + // // '@typescript-eslint/explicit-module-boundary-types': 'error', + // // '@typescript-eslint/no-array-delete': 'error', + // // '@typescript-eslint/no-base-to-string': 'error', + // // '@typescript-eslint/no-confusing-non-null-assertion': 'error', + // // '@typescript-eslint/no-confusing-void-expression': 'error', + // // '@typescript-eslint/no-duplicate-enum-values': 'error', + // // '@typescript-eslint/no-dynamic-delete': 'error', + // // 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ + // // 'error', + // // { + // // 'allow': [ + // // 'private-constructors' + // // ] + // // } + // // ], + // // '@typescript-eslint/no-empty-object-type': 'error', + // // '@typescript-eslint/no-explicit-any': 'error', + // // '@typescript-eslint/no-extra-non-null-assertion': 'error', + // // '@typescript-eslint/no-extraneous-class': 'error', + // // '@typescript-eslint/no-floating-promises': 'error', + // // '@typescript-eslint/no-for-in-array': 'error', + // // 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', + // // '@typescript-eslint/no-invalid-void-type': 'error', + // // 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', + // // '@typescript-eslint/no-misused-new': 'warn', + // // '@typescript-eslint/no-misused-promises': 'error', + // // '@typescript-eslint/no-mixed-enums': 'error', + // // '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + // // '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + // // '@typescript-eslint/no-non-null-assertion': 'error', + // // '@typescript-eslint/no-redundant-type-constituents': 'error', + // '@typescript-eslint/naming-convention': [ + // 'warn', + // { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, + // { 'selector': 'variable', 'filter': '^I.+Service$', 'format': ['PascalCase'], 'prefix': ['I'] }, + // { 'selector': 'enumMember', 'format': ['PascalCase'] }, + // { 'selector': 'typeAlias', 'format': ['PascalCase'], 'prefix': ['T'] }, + // { 'selector': 'interface', 'format': ['PascalCase'], 'prefix': ['I'] } + // ], + // // 'comma-dangle': ['warn', 'only-multiline'], + // // // // // TODO: @lego + // // // // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', + // // // // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations + // // // '@typescript-eslint/restrict-template-expressions': 'off', } }, ); diff --git a/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts b/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts index cf0d04ff8e4..0d21c680c2b 100644 --- a/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts +++ b/src/vs/editor/common/codecs/frontMatterCodec/parsers/frontMatterArray.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { VALID_SPACE_TOKENS } from '../constants.js'; +import { assert } from '../../../../../base/common/assert.js'; +import { PartialFrontMatterValue } from './frontMatterValue.js'; import { FrontMatterArray } from '../tokens/frontMatterArray.js'; import { assertDefined } from '../../../../../base/common/types.js'; import { FrontMatterValueToken } from '../tokens/frontMatterToken.js'; import { TSimpleDecoderToken } from '../../simpleCodec/simpleDecoder.js'; -import { assert, assertNever } from '../../../../../base/common/assert.js'; import { Comma, LeftBracket, RightBracket } from '../../simpleCodec/tokens/index.js'; -import { PartialFrontMatterValue, VALID_VALUE_START_TOKENS } from './frontMatterValue.js'; import { assertNotConsumed, ParserBase, TAcceptTokenResult } from '../../simpleCodec/parserBase.js'; /** @@ -22,13 +22,6 @@ const VALID_DELIMITER_TOKENS = Object.freeze([ Comma, ]); -// readonly (Exclude<(typeof VALID_SPACE_TOKENS[number] | typeof Comma), typeof VALID_VALUE_START_TOKENS[number]>)[] - -/** - * TODO: @legomushroom - */ -type IsUnion = T extends U ? never : never; - /** * Responsible for parsing an array syntax (or "inline sequence" * in YAML terms), e.g. `[1, '2', true, 2.54]` @@ -50,29 +43,6 @@ export class PartialFrontMatterArray extends ParserBase, - '', - ); - super([startToken]); } diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/comma.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/comma.ts index 3a1954a1202..ce5df1748ca 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/comma.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/comma.ts @@ -18,7 +18,7 @@ export class Comma extends SimpleToken<','> { /** * Return text representation of the token. */ - public override get text() { + public override get text(): ',' { return Comma.symbol; } diff --git a/src/vs/platform/prompts/test/common/utils/mock.ts b/src/vs/platform/prompts/test/common/utils/mock.ts index c34231bd5e5..3771b085c2e 100644 --- a/src/vs/platform/prompts/test/common/utils/mock.ts +++ b/src/vs/platform/prompts/test/common/utils/mock.ts @@ -11,8 +11,7 @@ import { assertOneOf } from '../../../../../base/common/types.js'; * If you need to mock an `Service`, please use {@link mockService} * instead which provides better type safety guarantees for the case. * - * @throws Reading non-overridden property or function - * on `TObject` throws an error. + * @throws Reading non-overridden property or function on `TObject` throws an error. */ export function mockObject( overrides: Partial, @@ -41,14 +40,15 @@ export function mockObject( `The '${key}' is not mocked.`, ); - // TODO: @legomushroom - add type assertion comment + // note! it's ok to type assert here, because of the explicit runtime + // assertion above // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return overrides[key as T] as TObject[T]; }, }); - // note! it's ok to `as TObject` here, because of the runtime checks - // in the `Proxy` getter + // note! it's ok to type assert here, because of the runtime checks in + // the `Proxy` getter // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return service as TObject; } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts index 5fa9591504d..ff2345c64c9 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/promptDecorationsProvider.ts @@ -10,7 +10,7 @@ import { FrontMatterDecoration } from './decorations/frontMatterDecoration.js'; import { toDisposable } from '../../../../../../../../../base/common/lifecycle.js'; import { Position } from '../../../../../../../../../editor/common/core/position.js'; import { BaseToken } from '../../../../../../../../../editor/common/codecs/baseToken.js'; -import { ProviderInstanceManagerBase, TProviderInstance } from '../providerInstanceManagerBase.js'; +import { ProviderInstanceManagerBase, TProviderClass } from '../providerInstanceManagerBase.js'; import { registerThemingParticipant } from '../../../../../../../../../platform/theme/common/themeService.js'; import { FrontMatterHeader } from '../../../../../../../../../editor/common/codecs/markdownExtensionsCodec/tokens/frontMatterHeader.js'; import { DecorationBase, ReactiveDecorationBase, type TDecorationClass, type TChangedDecorator } from './decorations/utils/index.js'; @@ -198,7 +198,7 @@ registerThemingParticipant((_theme, collector) => { * Provider for prompt syntax decorators on text models. */ export class PromptDecorationsProviderInstanceManager extends ProviderInstanceManagerBase { - protected override get InstanceClass(): TProviderInstance { + protected override get InstanceClass(): TProviderClass { return PromptDecorator; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts index b7084e159b1..fed5f75a187 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts @@ -7,7 +7,7 @@ import { IPromptsService } from '../../../service/types.js'; import { ProviderInstanceBase } from './providerInstanceBase.js'; import { assertNever } from '../../../../../../../../base/common/assert.js'; import { ITextModel } from '../../../../../../../../editor/common/model.js'; -import { ProviderInstanceManagerBase, TProviderInstance } from './providerInstanceManagerBase.js'; +import { ProviderInstanceManagerBase, TProviderClass } from './providerInstanceManagerBase.js'; import { TDiagnostic, PromptMetadataError, PromptMetadataWarning } from '../../../parsers/promptHeader/diagnostics.js'; import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../../../../../platform/markers/common/markers.js'; @@ -100,7 +100,7 @@ const toMarker = ( * classes for each specific editor text model. */ export class PromptHeaderDiagnosticsInstanceManager extends ProviderInstanceManagerBase { - protected override get InstanceClass(): TProviderInstance { + protected override get InstanceClass(): TProviderClass { return PromptHeaderDiagnosticsProvider; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts index fe40077ceba..08caa894905 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts @@ -10,7 +10,7 @@ import { assert } from '../../../../../../../../base/common/assert.js'; import { NotPromptFile } from '../../../../promptFileReferenceErrors.js'; import { ITextModel } from '../../../../../../../../editor/common/model.js'; import { assertDefined } from '../../../../../../../../base/common/types.js'; -import { ProviderInstanceManagerBase, TProviderInstance } from './providerInstanceManagerBase.js'; +import { ProviderInstanceManagerBase, TProviderClass } from './providerInstanceManagerBase.js'; import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../../../../../platform/markers/common/markers.js'; /** @@ -125,7 +125,7 @@ const toMarker = ( * classes for each specific editor text model. */ export class PromptLinkDiagnosticsInstanceManager extends ProviderInstanceManagerBase { - protected override get InstanceClass(): TProviderInstance { + protected override get InstanceClass(): TProviderClass { return PromptLinkDiagnosticsProvider; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts index 68670a2b11c..b1a769ae3ce 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceManagerBase.ts @@ -25,9 +25,9 @@ export interface IPromptFileEditor extends IEditor { } /** - * TODO: @legomushroom + * Type for a class that can create a new provider instance. */ -export type TProviderInstance = new (editor: ITextModel, ...args: any[]) => TInstance; +export type TProviderClass = new (editor: ITextModel, ...args: any[]) => TInstance; /** * A generic base class that manages creation and disposal of {@link TInstance} @@ -42,7 +42,7 @@ export abstract class ProviderInstanceManagerBase; + protected abstract get InstanceClass(): TProviderClass; constructor( @IModelService modelService: IModelService, From 59b0b30683d17f2ba96a1ffa3268cb41dd3b724b Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 16:36:18 -0700 Subject: [PATCH 43/90] add more eslint rules and fix related linter issues --- eslint.config.js | 102 +++++++++--------- .../codecs/markdownCodec/markdownDecoder.ts | 4 +- .../editor/common/codecs/utils/tokenStream.ts | 16 ++- .../promptSyntax/codecs/chatPromptDecoder.ts | 25 +++-- .../filePromptContentsProvider.ts | 6 +- .../decorations/frontMatterDecoration.ts | 16 +-- .../frontMatterMarkerDecoration.ts | 12 +-- .../utils/reactiveDecorationBase.ts | 8 +- .../decorations/utils/types.d.ts | 8 +- .../providers/decorationsProvider/types.ts | 6 +- .../parsers/promptHeader/header.ts | 6 +- 11 files changed, 114 insertions(+), 95 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 46af66d6fd6..4811f9625e9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1471,61 +1471,61 @@ export default tseslint.config( allowDirectConstAssertionInArrowFunctions: false, }, ], - // '@typescript-eslint/explicit-member-accessibility': [ + '@typescript-eslint/explicit-member-accessibility': [ + 'error', + { + accessibility: 'explicit', + ignoredMethodNames: ['constructor'], + }, + ], + 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/ban-ts-comment': 'error', + 'default-param-last': 'off', '@typescript-eslint/default-param-last': 'error', + 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/explicit-module-boundary-types': 'error', + '@typescript-eslint/no-array-delete': 'error', + '@typescript-eslint/no-base-to-string': 'error', + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + '@typescript-eslint/no-confusing-void-expression': 'error', + // '@typescript-eslint/no-duplicate-enum-values': 'error', + // '@typescript-eslint/no-dynamic-delete': 'error', + // 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ // 'error', // { - // accessibility: 'explicit', - // ignoredMethodNames: ['constructor'], + // 'allow': [ + // 'private-constructors' + // ] // } // ], - // 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', - // '@typescript-eslint/ban-ts-comment': 'error', - // 'default-param-last': 'off', '@typescript-eslint/default-param-last': 'error', - // 'no-array-constructor': 'off', '@typescript-eslint/no-array-constructor': 'error', - // // '@typescript-eslint/explicit-module-boundary-types': 'error', - // // '@typescript-eslint/no-array-delete': 'error', - // // '@typescript-eslint/no-base-to-string': 'error', - // // '@typescript-eslint/no-confusing-non-null-assertion': 'error', - // // '@typescript-eslint/no-confusing-void-expression': 'error', - // // '@typescript-eslint/no-duplicate-enum-values': 'error', - // // '@typescript-eslint/no-dynamic-delete': 'error', - // // 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ - // // 'error', - // // { - // // 'allow': [ - // // 'private-constructors' - // // ] - // // } - // // ], - // // '@typescript-eslint/no-empty-object-type': 'error', - // // '@typescript-eslint/no-explicit-any': 'error', - // // '@typescript-eslint/no-extra-non-null-assertion': 'error', - // // '@typescript-eslint/no-extraneous-class': 'error', - // // '@typescript-eslint/no-floating-promises': 'error', - // // '@typescript-eslint/no-for-in-array': 'error', - // // 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', - // // '@typescript-eslint/no-invalid-void-type': 'error', - // // 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', - // // '@typescript-eslint/no-misused-new': 'warn', - // // '@typescript-eslint/no-misused-promises': 'error', - // // '@typescript-eslint/no-mixed-enums': 'error', - // // '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', - // // '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', - // // '@typescript-eslint/no-non-null-assertion': 'error', - // // '@typescript-eslint/no-redundant-type-constituents': 'error', - // '@typescript-eslint/naming-convention': [ - // 'warn', - // { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, - // { 'selector': 'variable', 'filter': '^I.+Service$', 'format': ['PascalCase'], 'prefix': ['I'] }, - // { 'selector': 'enumMember', 'format': ['PascalCase'] }, - // { 'selector': 'typeAlias', 'format': ['PascalCase'], 'prefix': ['T'] }, - // { 'selector': 'interface', 'format': ['PascalCase'], 'prefix': ['I'] } - // ], - // // 'comma-dangle': ['warn', 'only-multiline'], - // // // // // TODO: @lego - // // // // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', - // // // // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations - // // // '@typescript-eslint/restrict-template-expressions': 'off', + // '@typescript-eslint/no-empty-object-type': 'error', + // '@typescript-eslint/no-explicit-any': 'error', + // '@typescript-eslint/no-extra-non-null-assertion': 'error', + // '@typescript-eslint/no-extraneous-class': 'error', + // '@typescript-eslint/no-floating-promises': 'error', + // '@typescript-eslint/no-for-in-array': 'error', + // 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', + // '@typescript-eslint/no-invalid-void-type': 'error', + // 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', + // '@typescript-eslint/no-misused-new': 'warn', + // '@typescript-eslint/no-misused-promises': 'error', + // '@typescript-eslint/no-mixed-enums': 'error', + // '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + // '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + // '@typescript-eslint/no-non-null-assertion': 'error', + // '@typescript-eslint/no-redundant-type-constituents': 'error', + '@typescript-eslint/naming-convention': [ + 'warn', + { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, + { 'selector': 'variable', 'filter': '^I.+Service$', 'format': ['PascalCase'], 'prefix': ['I'] }, + { 'selector': 'enumMember', 'format': ['PascalCase'] }, + { 'selector': 'typeAlias', 'format': ['PascalCase'], 'prefix': ['T'] }, + { 'selector': 'interface', 'format': ['PascalCase'], 'prefix': ['I'] } + ], + 'comma-dangle': ['warn', 'only-multiline'], + // // // // TODO: @lego + // // // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', + // // // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations + // // '@typescript-eslint/restrict-template-expressions': 'off', } }, ); diff --git a/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts index 7dd32661181..bf98444794d 100644 --- a/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts +++ b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts @@ -116,7 +116,9 @@ export class MarkdownDecoder extends BaseDecoder extends ObservableDisposable imple public pause(): void { this.stopStream(); + this.stream.pause(); - return this.stream.pause(); + return; } public resume(): void { this.startStream(); + this.stream.resume(); - return this.stream.resume(); + return; } public destroy(): void { @@ -138,7 +140,9 @@ export class TokenStream extends ObservableDisposable imple } public removeListener(event: string, callback: Function): void { - return this.stream.removeListener(event, callback); + this.stream.removeListener(event, callback); + + return; } public on(event: 'data', callback: (data: T) => void): void; @@ -155,11 +159,13 @@ export class TokenStream extends ObservableDisposable imple } if (event === 'error') { - return this.stream.on(event, callback); + this.stream.on(event, callback); + return; } if (event === 'end') { - return this.stream.on(event, callback); + this.stream.on(event, callback); + return; } assertNever( diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts index 6aba6b0ead9..cad0bfaf391 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts @@ -7,7 +7,7 @@ import { PromptToken } from './tokens/promptToken.js'; import { PromptAtMention } from './tokens/promptAtMention.js'; import { VSBuffer } from '../../../../../../base/common/buffer.js'; import { PromptSlashCommand } from './tokens/promptSlashCommand.js'; -import { assertNever } from '../../../../../../base/common/assert.js'; +import { assert, assertNever } from '../../../../../../base/common/assert.js'; import { ReadableStream } from '../../../../../../base/common/stream.js'; import { PartialPromptAtMention } from './parsers/promptAtMentionParser.js'; import { PromptTemplateVariable } from './tokens/promptTemplateVariable.js'; @@ -143,26 +143,33 @@ export class ChatPromptDecoder extends BaseDecoder { /** * Main, default CSS class name of the decoration. */ - readonly main: T; + readonly Main: T; /** * CSS class name of the decoration for the `inline`(text) styles. */ - readonly inline: T; + readonly Inline: T; /** * main CSS class name of the decoration for the `inactive` * decoration state. */ - readonly mainInactive: T; + readonly MainInactive: T; /** * CSS class name of the decoration for the `inline`(text) * styles when decoration is in the `inactive` state. */ - readonly inlineInactive: T; + readonly InlineInactive: T; } /** diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/types.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/types.ts index f1634345088..366a0c33150 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/types.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/decorationsProvider/types.ts @@ -28,12 +28,12 @@ export enum DecorationClassNames { /** * CSS class name for `default` prompt syntax decoration. */ - default = 'prompt-decoration', + Default = 'prompt-decoration', /** * CSS class name for `file reference` prompt syntax decoration. */ - fileReference = DecorationClassNames.default, + FileReference = DecorationClassNames.Default, } /** @@ -44,5 +44,5 @@ export enum CssClassModifiers { * CSS class modifier for `active` state of * a `reactive` prompt syntax decoration. */ - inactive = '.prompt-decoration-inactive', + Inactive = '.prompt-decoration-inactive', } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts index 8627440ce26..c8bd574a6e2 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts @@ -170,7 +170,8 @@ export class PromptHeader extends Disposable { this.meta.tools = toolsMetadata; this.recordNames.add(recordName); - return this.validateToolsAndModeCompatibility(); + this.validateToolsAndModeCompatibility(); + return; } // if the record might be a "mode" metadata @@ -183,7 +184,8 @@ export class PromptHeader extends Disposable { this.meta.mode = modeMetadata; this.recordNames.add(recordName); - return this.validateToolsAndModeCompatibility(); + this.validateToolsAndModeCompatibility(); + return; } // if the record might be a "applyTo" metadata From b49c8c82459a9c497ca207c09fcdd7bb20330111 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 18:18:32 -0700 Subject: [PATCH 44/90] more eslint rules and linter error fixes --- eslint.config.js | 43 ++++++++----------- .../common/codecs/simpleCodec/parserBase.ts | 2 +- .../editor/common/codecs/utils/tokenStream.ts | 21 +++++---- .../textModelContentsProvider.ts | 6 +-- .../contributions/configMigration.ts | 29 +++++++++---- .../common/promptSyntax/utils/treeUtils.ts | 6 +-- 6 files changed, 60 insertions(+), 47 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 4811f9625e9..5f42abbc744 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1487,32 +1487,27 @@ export default tseslint.config( '@typescript-eslint/no-base-to-string': 'error', '@typescript-eslint/no-confusing-non-null-assertion': 'error', '@typescript-eslint/no-confusing-void-expression': 'error', - // '@typescript-eslint/no-duplicate-enum-values': 'error', - // '@typescript-eslint/no-dynamic-delete': 'error', - // 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ - // 'error', - // { - // 'allow': [ - // 'private-constructors' - // ] - // } - // ], - // '@typescript-eslint/no-empty-object-type': 'error', - // '@typescript-eslint/no-explicit-any': 'error', - // '@typescript-eslint/no-extra-non-null-assertion': 'error', - // '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-duplicate-enum-values': 'error', + '@typescript-eslint/no-dynamic-delete': 'error', + 'no-empty-function': 'off', '@typescript-eslint/no-empty-function': [ + 'error', { 'allow': ['private-constructors'] } + ], + '@typescript-eslint/no-empty-object-type': 'error', + '@typescript-eslint/no-explicit-any': ['error', { 'ignoreRestArgs': true }], + '@typescript-eslint/no-extra-non-null-assertion': 'error', + '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-for-in-array': 'error', + 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', + '@typescript-eslint/no-misused-new': 'warn', + '@typescript-eslint/no-mixed-enums': 'error', // '@typescript-eslint/no-floating-promises': 'error', - // '@typescript-eslint/no-for-in-array': 'error', - // 'no-implied-eval': 'off', '@typescript-eslint/no-implied-eval': 'error', - // '@typescript-eslint/no-invalid-void-type': 'error', - // 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', - // '@typescript-eslint/no-misused-new': 'warn', // '@typescript-eslint/no-misused-promises': 'error', - // '@typescript-eslint/no-mixed-enums': 'error', - // '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', - // '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', - // '@typescript-eslint/no-non-null-assertion': 'error', - // '@typescript-eslint/no-redundant-type-constituents': 'error', + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-redundant-type-constituents': 'error', '@typescript-eslint/naming-convention': [ 'warn', { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, diff --git a/src/vs/editor/common/codecs/simpleCodec/parserBase.ts b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts index afd3051309e..3df13577159 100644 --- a/src/vs/editor/common/codecs/simpleCodec/parserBase.ts +++ b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts @@ -111,7 +111,7 @@ export abstract class ParserBase { * * @throws the resulting decorated method throws if the parser object was already consumed. */ -export function assertNotConsumed>( +export function assertNotConsumed>( _target: T, propertyKey: 'accept', descriptor: PropertyDescriptor, diff --git a/src/vs/editor/common/codecs/utils/tokenStream.ts b/src/vs/editor/common/codecs/utils/tokenStream.ts index a09c3a9858c..94585882bb6 100644 --- a/src/vs/editor/common/codecs/utils/tokenStream.ts +++ b/src/vs/editor/common/codecs/utils/tokenStream.ts @@ -64,7 +64,7 @@ export class TokenStream extends ObservableDisposable imple } // periodically send tokens to the stream - this.interval = setInterval(() => { + this.interval = setInterval(async () => { if (this.tokensLeft === 0) { clearInterval(this.interval); delete this.interval; @@ -72,7 +72,7 @@ export class TokenStream extends ObservableDisposable imple return; } - this.sendTokens(); + await this.sendTokens(); }, 1); return this; @@ -95,9 +95,9 @@ export class TokenStream extends ObservableDisposable imple /** * Sends a provided number of tokens to the stream. */ - private sendTokens( + private async sendTokens( tokensCount: number = 25, - ): void { + ): Promise { if (this.tokensLeft <= 0) { return; } @@ -110,9 +110,14 @@ export class TokenStream extends ObservableDisposable imple `Token index '${this.index}' is out of bounds.`, ); - this.stream.write(this.tokens[this.index]); - this.index++; - tokensToSend--; + try { + await this.stream.write(this.tokens[this.index]); + this.index++; + tokensToSend--; + } catch { + this.stopStream(); + return; + } } // if sent all tokens, end the stream immediately @@ -148,7 +153,7 @@ export class TokenStream extends ObservableDisposable imple public on(event: 'data', callback: (data: T) => void): void; public on(event: 'error', callback: (err: Error) => void): void; public on(event: 'end', callback: () => void): void; - public on(event: 'data' | 'error' | 'end', callback: (arg?: any) => void): void { + public on(event: 'data' | 'error' | 'end', callback: (...args: any[]) => void): void { if (event === 'data') { this.stream.on(event, callback); // this is the convention of the readable stream, - when diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index 5ec7f47ac1b..e6e561f1c6e 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -79,7 +79,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase { + const interval = setInterval(async () => { // if we have written all lines or lines count is zero, // end the stream and stop the interval timer if (i >= linesCount) { @@ -99,14 +99,14 @@ export class TextModelContentsProvider extends PromptContentsProviderBase { + this.logService.warn('failed to migrate config setting value.', error); + }); + } + + /** + * The main function that implements the migration logic. + */ + private async migrateConfig(): Promise { + const value = await this.configService.getValue(CONFIG_KEY); // if setting is not set, nothing to do if ((value === undefined) || (value === null)) { @@ -49,8 +62,8 @@ export class ConfigMigration implements IWorkbenchContribution { locationsValue[trimmedValue] = true; } - configService.updateValue(CONFIG_KEY, true); - configService.updateValue(PROMPT_LOCATIONS_CONFIG_KEY, locationsValue); + await this.configService.updateValue(CONFIG_KEY, true); + await this.configService.updateValue(PROMPT_LOCATIONS_CONFIG_KEY, locationsValue); return; } @@ -82,8 +95,8 @@ export class ConfigMigration implements IWorkbenchContribution { locationsValue[trimmedValue] = enabled; } - configService.updateValue(CONFIG_KEY, true); - configService.updateValue(PROMPT_LOCATIONS_CONFIG_KEY, locationsValue); + await this.configService.updateValue(CONFIG_KEY, true); + await this.configService.updateValue(PROMPT_LOCATIONS_CONFIG_KEY, locationsValue); return; } @@ -99,8 +112,8 @@ export class ConfigMigration implements IWorkbenchContribution { `String value must not be a boolean, got '${value}'.`, ); - configService.updateValue(CONFIG_KEY, true); - configService.updateValue(PROMPT_LOCATIONS_CONFIG_KEY, { [value]: true }); + await this.configService.updateValue(CONFIG_KEY, true); + await this.configService.updateValue(PROMPT_LOCATIONS_CONFIG_KEY, { [value]: true }); return; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts index 6c596c3ab7f..cdc81e77ec4 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/utils/treeUtils.ts @@ -127,14 +127,14 @@ export const map = < * Type for a rest parameters of function, excluding * the first argument. */ -type TRestParameters any> = - T extends (first: any, ...rest: infer R) => any ? R : never; +type TRestParameters unknown> = + T extends (first: Parameters[0], ...rest: infer R) => unknown ? R : never; /** * Type for a curried function. * See {@link curry} for more info. */ -type TCurriedFunction any> = ((...args: TRestParameters) => ReturnType); +type TCurriedFunction unknown> = ((...args: TRestParameters) => ReturnType); /** * Curry a provided function with the first argument. From 03c1c34cda247722d13f5860094d97989f2f49a9 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 19:04:40 -0700 Subject: [PATCH 45/90] refactor token stream to remove floating promises --- eslint.config.js | 4 +- .../editor/common/codecs/utils/tokenStream.ts | 111 ++++++++---------- .../textModelContentsProvider.ts | 6 +- .../promptDecorationsProvider.ts | 4 +- .../promptHeaderDiagnosticsProvider.ts | 5 +- .../promptLinkDiagnosticsProvider.ts | 5 +- .../providers/providerInstanceBase.ts | 2 +- .../promptSyntax/parsers/basePromptParser.ts | 14 ++- 8 files changed, 70 insertions(+), 81 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 5f42abbc744..05993dd88aa 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1502,8 +1502,8 @@ export default tseslint.config( 'no-loop-func': 'off', '@typescript-eslint/no-loop-func': 'error', '@typescript-eslint/no-misused-new': 'warn', '@typescript-eslint/no-mixed-enums': 'error', - // '@typescript-eslint/no-floating-promises': 'error', - // '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-misused-promises': 'error', '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', '@typescript-eslint/no-non-null-assertion': 'error', diff --git a/src/vs/editor/common/codecs/utils/tokenStream.ts b/src/vs/editor/common/codecs/utils/tokenStream.ts index 94585882bb6..960fdb81618 100644 --- a/src/vs/editor/common/codecs/utils/tokenStream.ts +++ b/src/vs/editor/common/codecs/utils/tokenStream.ts @@ -4,78 +4,71 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../baseToken.js'; -import { assert, assertNever } from '../../../../base/common/assert.js'; +import { assertNever } from '../../../../base/common/assert.js'; +import { assertDefined } from '../../../../base/common/types.js'; import { ObservableDisposable } from '../../../../base/common/observableDisposable.js'; import { newWriteableStream, WriteableStream, ReadableStream } from '../../../../base/common/stream.js'; /** * A readable stream of provided tokens. */ +// TODO: @legomushroom - add unit tests export class TokenStream extends ObservableDisposable implements ReadableStream { /** * Underlying writable stream instance. */ private readonly stream: WriteableStream; - /** - * Index of the next token to be sent. - */ - private index: number; - /** * Interval reference that is used to periodically send * tokens to the stream in the background. */ - private interval: ReturnType | undefined; + private interval: ReturnType | undefined; /** - * Number of tokens left to be sent. + * TODO: @legomushroom */ - private get tokensLeft(): number { - return this.tokens.length - this.index; - } + private readonly tokens: T[]; constructor( - private readonly tokens: readonly T[], + tokens: readonly T[], ) { super(); this.stream = newWriteableStream(null); - this.index = 0; + // copy and reverse the tokens list so we can pop items from its e end + this.tokens = [...tokens].reverse(); // send couple of tokens immediately - this.sendTokens(); + this.send(false); } /** - * Start periodically sending tokens to the stream - * asynchronously in the background. + * TODO: @legomushroom */ - public startStream(): this { - // already running, noop - if (this.interval !== undefined) { - return this; - } + public send( + play: boolean = true, + ): void { + this.sendTokens() + .then(() => { + if (this.tokens.length === 0) { + this.stream.end(); + this.stopStream(); + return; + } - // no tokens to send, end the stream immediately - if (this.tokens.length === 0) { - this.stream.end(); - return this; - } + if (play === false) { + this.stopStream(); + return; + } - // periodically send tokens to the stream - this.interval = setInterval(async () => { - if (this.tokensLeft === 0) { - clearInterval(this.interval); - delete this.interval; - - return; - } - - await this.sendTokens(); - }, 1); - - return this; + this.interval = setImmediate(this.send.bind(this)); + }) + .catch(() => { + this.stream.destroy(); + this.stream.end(); + this.stopStream(); + }); } /** @@ -86,7 +79,7 @@ export class TokenStream extends ObservableDisposable imple return this; } - clearInterval(this.interval); + clearImmediate(this.interval); delete this.interval; return this; @@ -98,32 +91,28 @@ export class TokenStream extends ObservableDisposable imple private async sendTokens( tokensCount: number = 25, ): Promise { - if (this.tokensLeft <= 0) { - return; - } - - // send up to 10 tokens at a time - let tokensToSend = Math.min(this.tokensLeft, tokensCount); - while (tokensToSend > 0) { - assert( - this.index < this.tokens.length, - `Token index '${this.index}' is out of bounds.`, - ); + // if (this.tokens.length === 0) { + // return; + // } + // send up to 'tokensCount' tokens at a time + while ((tokensCount > 0) && (this.tokens.length > 0)) { try { - await this.stream.write(this.tokens[this.index]); - this.index++; - tokensToSend--; + const token = this.tokens.pop(); + + assertDefined( + token, + `Token must be defined. Tokens left: ${this.tokens.length}.`, + ); + + await this.stream.write(token); } catch { + this.stream.destroy(); + this.stream.end(); this.stopStream(); return; } } - - // if sent all tokens, end the stream immediately - if (this.tokensLeft === 0) { - this.stream.end(); - } } public pause(): void { @@ -134,7 +123,7 @@ export class TokenStream extends ObservableDisposable imple } public resume(): void { - this.startStream(); + this.send(); this.stream.resume(); return; @@ -158,7 +147,7 @@ export class TokenStream extends ObservableDisposable imple this.stream.on(event, callback); // this is the convention of the readable stream, - when // the `data` event is registered, the stream is started - this.startStream(); + this.send(); return; } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index e6e561f1c6e..5ec7f47ac1b 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -79,7 +79,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase { + const interval = setInterval(() => { // if we have written all lines or lines count is zero, // end the stream and stop the interval timer if (i >= linesCount) { @@ -99,14 +99,14 @@ export class TextModelContentsProvider extends PromptContentsProviderBase { + ): this { // by the time the promise above completes, either this object // or the text model might be already has been disposed if (this.disposed || this.model.isDisposed()) { diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts index fed5f75a187..c0046e8d178 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptHeaderDiagnosticsProvider.ts @@ -32,10 +32,7 @@ class PromptHeaderDiagnosticsProvider extends ProviderInstanceBase { /** * Update diagnostic markers for the current editor. */ - protected override async onPromptSettled(): Promise { - // ensure that parsing process is settled - await this.parser.allSettled(); - + protected override onPromptSettled(): this { // clean up all previously added markers this.markerService.remove(MARKERS_OWNER_ID, [this.model.uri]); diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts index 08caa894905..1d43958165f 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/promptLinkDiagnosticsProvider.ts @@ -33,10 +33,7 @@ class PromptLinkDiagnosticsProvider extends ProviderInstanceBase { /** * Update diagnostic markers for the current editor. */ - protected override async onPromptSettled(): Promise { - // ensure that parsing process is settled - await this.parser.allSettled(); - + protected override onPromptSettled(): this { // clean up all previously added markers this.markerService.remove(MARKERS_OWNER_ID, [this.model.uri]); diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceBase.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceBase.ts index f42d4763485..c23584b939c 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceBase.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contributions/languageFeatures/providers/providerInstanceBase.ts @@ -14,7 +14,7 @@ export abstract class ProviderInstanceBase extends ObservableDisposable { /** * Function that is called when the prompt parser is settled. */ - protected abstract onPromptSettled(error: Error | undefined): Promise; + protected abstract onPromptSettled(error: Error | undefined): this; /** * Returns a string representation of this object. diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts index 888f5f669ac..42db59f8f44 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts @@ -260,7 +260,7 @@ export class BasePromptParser seenReferences, ); this._onUpdate.fire(); - this.firstParseResult.complete(); + this.firstParseResult.end(); return this; } @@ -276,7 +276,7 @@ export class BasePromptParser this.onContentsChanged(streamOrError, seenReferences); // indicate that we've received at least one `onContentChanged` event - this.firstParseResult.complete(); + this.firstParseResult.end(); }), ); @@ -978,8 +978,14 @@ class FirstParseResult extends DeferredPromise { /** * Complete the underlying promise. */ - public override complete(): Promise { + public end(): void { this._gotResult = true; - return super.complete(void 0); + super.complete(void 0) + .catch(() => { + // noop + // TODO: @legomushroom + }); + + return; } } From 9394f4cc77e0fab1e18199d733ac6d09f4863b19 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 5 May 2025 19:51:09 -0700 Subject: [PATCH 46/90] text model contents provider to remove floating promises --- .../frontMatterCodec/frontMatterDecoder.ts | 6 +- .../utils/{tokenStream.ts => stream.ts} | 63 +++++++----- .../textModelContentsProvider.ts | 99 ++++++------------- .../parsers/promptHeader/header.ts | 4 +- 4 files changed, 78 insertions(+), 94 deletions(-) rename src/vs/editor/common/codecs/utils/{tokenStream.ts => stream.ts} (76%) diff --git a/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts b/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts index 02db7f7fca7..c24d6b424d9 100644 --- a/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts +++ b/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts @@ -5,7 +5,7 @@ import { VALID_SPACE_TOKENS } from './constants.js'; import { Word } from '../simpleCodec/tokens/index.js'; -import { TokenStream } from '../utils/tokenStream.js'; +import { Stream } from '../utils/stream.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { ReadableStream } from '../../../../base/common/stream.js'; import { FrontMatterToken, FrontMatterRecord } from './tokens/index.js'; @@ -29,9 +29,9 @@ export class FrontMatterDecoder extends BaseDecoder | TokenStream, + stream: ReadableStream | Stream, ) { - if (stream instanceof TokenStream) { + if (stream instanceof Stream) { super(stream); return; diff --git a/src/vs/editor/common/codecs/utils/tokenStream.ts b/src/vs/editor/common/codecs/utils/stream.ts similarity index 76% rename from src/vs/editor/common/codecs/utils/tokenStream.ts rename to src/vs/editor/common/codecs/utils/stream.ts index 960fdb81618..6cb9f0ba62f 100644 --- a/src/vs/editor/common/codecs/utils/tokenStream.ts +++ b/src/vs/editor/common/codecs/utils/stream.ts @@ -3,17 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BaseToken } from '../baseToken.js'; import { assertNever } from '../../../../base/common/assert.js'; -import { assertDefined } from '../../../../base/common/types.js'; import { ObservableDisposable } from '../../../../base/common/observableDisposable.js'; import { newWriteableStream, WriteableStream, ReadableStream } from '../../../../base/common/stream.js'; +/** + * TODO: @legomushroom + */ +export const arrayToGenerator = >(array: readonly T[]): Generator => { + return (function* (): Generator { + for (const item of array) { + yield item; + } + })(); +}; + /** * A readable stream of provided tokens. */ // TODO: @legomushroom - add unit tests -export class TokenStream extends ObservableDisposable implements ReadableStream { +export class Stream extends ObservableDisposable implements ReadableStream { /** * Underlying writable stream instance. */ @@ -25,20 +34,25 @@ export class TokenStream extends ObservableDisposable imple */ private interval: ReturnType | undefined; + // TODO: @legomushroom + private ended: boolean = false; + /** * TODO: @legomushroom */ - private readonly tokens: T[]; + public static fromArray( + array: readonly T[], + ): Stream { + return new Stream(arrayToGenerator(array)); + } constructor( - tokens: readonly T[], + private readonly data: Generator, ) { super(); this.stream = newWriteableStream(null); - // copy and reverse the tokens list so we can pop items from its e end - this.tokens = [...tokens].reverse(); // send couple of tokens immediately this.send(false); } @@ -49,9 +63,14 @@ export class TokenStream extends ObservableDisposable imple public send( play: boolean = true, ): void { + // TODO: @legomushroom - throw instead? + if (this.ended) { + return; + } + this.sendTokens() .then(() => { - if (this.tokens.length === 0) { + if (this.ended) { this.stream.end(); this.stopStream(); return; @@ -91,25 +110,25 @@ export class TokenStream extends ObservableDisposable imple private async sendTokens( tokensCount: number = 25, ): Promise { - // if (this.tokens.length === 0) { - // return; - // } - // send up to 'tokensCount' tokens at a time - while ((tokensCount > 0) && (this.tokens.length > 0)) { + // let token = this.data.read(); + while (tokensCount > 0) { try { - const token = this.tokens.pop(); + const token = this.data.next(); + if (token.done) { + this.ended = true; + this.stopStream(); + this.stream.end(); + break; + } - assertDefined( - token, - `Token must be defined. Tokens left: ${this.tokens.length}.`, - ); - - await this.stream.write(token); + await this.stream.write(token.value); + tokensCount--; } catch { - this.stream.destroy(); - this.stream.end(); this.stopStream(); + this.stream.destroy(); + // TODO: @legomushroom - needed? + this.stream.end(); return; } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index 5ec7f47ac1b..3ab8255a6a9 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -7,15 +7,41 @@ import { IPromptContentsProvider } from './types.js'; import { URI } from '../../../../../../base/common/uri.js'; import { VSBuffer } from '../../../../../../base/common/buffer.js'; import { ITextModel } from '../../../../../../editor/common/model.js'; -import { ILogService } from '../../../../../../platform/log/common/log.js'; -import { CancellationError } from '../../../../../../base/common/errors.js'; +import { ReadableStream } from '../../../../../../base/common/stream.js'; import { FilePromptContentProvider } from './filePromptContentsProvider.js'; import { TextModel } from '../../../../../../editor/common/model/textModel.js'; +import { Stream } from '../../../../../../editor/common/codecs/utils/stream.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; -import { newWriteableStream, ReadableStream } from '../../../../../../base/common/stream.js'; import { IModelContentChangedEvent } from '../../../../../../editor/common/textModelEvents.js'; -import { IPromptContentsProviderOptions, PromptContentsProviderBase } from './promptContentsProviderBase.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { IPromptContentsProviderOptions, PromptContentsProviderBase } from './promptContentsProviderBase.js'; + +/** + * TODO: @legomushroom + */ +export const modelToGenerator = (model: ITextModel): Generator => { + return (function* (): Generator { + const totalLines = model.getLineCount(); + let currentLine = 1; + + while (currentLine <= totalLines) { + if (model.isDisposed()) { + return undefined; + } + + yield VSBuffer.fromString( + model.getLineContent(currentLine), + ); + if (currentLine !== totalLines) { + yield VSBuffer.fromString( + model.getEOL(), + ); + } + + currentLine++; + } + })(); +}; /** * Prompt contents provider for a {@link ITextModel} instance. @@ -40,7 +66,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase, @IInstantiationService private readonly initService: IInstantiationService, - @ILogService private readonly logService: ILogService, + // @ILogService private readonly logService: ILogService, ) { super(options); @@ -63,68 +89,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase> { - const stream = newWriteableStream(null); - - // the `getLineCount`method throws is model is already disposed - // hence to be extra safe, we check the model state before getting - // the number of available lines in the text model - if (this.model.isDisposed()) { - stream.end(); - stream.destroy(); - - return stream; - } - - // provide the changed lines to the stream incrementally and asynchronously - // to avoid blocking the main thread and save system resources used - let i = 1; - const linesCount = this.model.getLineCount(); - const interval = setInterval(() => { - // if we have written all lines or lines count is zero, - // end the stream and stop the interval timer - if (i >= linesCount) { - clearInterval(interval); - stream.end(); - stream.destroy(); - } - - // if model was disposed or cancellation was requested, - // end the stream with an error and stop the interval timer - if (this.model.isDisposed() || cancellationToken?.isCancellationRequested) { - clearInterval(interval); - stream.error(new CancellationError()); - stream.destroy(); - return; - } - - try { - // write the current line to the stream - stream.write( - VSBuffer.fromString(this.model.getLineContent(i)), - ); - - // for all lines except the last one, write the EOL character - // to separate the lines in the stream - if (i !== linesCount) { - stream.write( - VSBuffer.fromString(this.model.getEOL()), - ); - } - } catch (error) { - this.logService.error( - [ - '[text model contents provider]: ', - `Failed to write line #${i} of text model '${this.uri.path}' to stream: `, - ].join(''), - error, - ); - } - - // use the next line in the next iteration - i++; - }, 1); - - return stream; + return new Stream(modelToGenerator(this.model)); } public override createNew( diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts index c8bd574a6e2..5ac2e5a053a 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts @@ -11,7 +11,7 @@ import { assertDefined } from '../../../../../../../base/common/types.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { Text } from '../../../../../../../editor/common/codecs/textToken.js'; import { PromptMetadataError, PromptMetadataWarning, TDiagnostic } from './diagnostics.js'; -import { TokenStream } from '../../../../../../../editor/common/codecs/utils/tokenStream.js'; +import { Stream } from '../../../../../../../editor/common/codecs/utils/stream.js'; import { SimpleToken } from '../../../../../../../editor/common/codecs/simpleCodec/tokens/index.js'; import { PromptToolsMetadata, PromptModeMetadata, PromptDescriptionMetadata } from './metadata/index.js'; import { FrontMatterRecord } from '../../../../../../../editor/common/codecs/frontMatterCodec/tokens/index.js'; @@ -94,7 +94,7 @@ export class PromptHeader extends Disposable { this.stream = this._register( new FrontMatterDecoder( - new TokenStream(contentsToken.tokens), + Stream.fromArray(contentsToken.tokens), ), ); this.stream.onData(this.onData.bind(this)); From f15d50d2218ca9d0289b7b77001e5667fcae1326 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 09:59:25 -0700 Subject: [PATCH 47/90] cleanup eslint config, fix more linter failures --- eslint.config.js | 14 +- src/vs/editor/common/codecs/utils/stream.ts | 130 +++++++++++---- .../common/codecs/testUtils/randomTokens.ts | 152 ++++++++++++++++++ .../codecs/tokens/compositeToken.test.ts | 146 +---------------- .../prompts/test/common/utils/mock.test.ts | 2 +- .../textModelContentsProvider.ts | 31 +--- 6 files changed, 258 insertions(+), 217 deletions(-) create mode 100644 src/vs/editor/test/common/codecs/testUtils/randomTokens.ts diff --git a/eslint.config.js b/eslint.config.js index 05993dd88aa..046a7a97cfb 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1435,14 +1435,8 @@ export default tseslint.config( '@typescript-eslint/prefer-readonly': 'warn', } }, - // TODO: @lego + // Prompt files related code { - // TODO: @lego - // extends: [ - // ...tseslint.configs.recommendedTypeChecked, - // // TODO: @lego - // // ...tseslint.configs.strictTypeChecked, - // ], files: [ 'src/vs/platform/prompts/**/*.ts', 'src/vs/editor/common/codecs/**/*.ts', @@ -1451,8 +1445,8 @@ export default tseslint.config( languageOptions: { parser: tseslint.parser, parserOptions: { - // TODO: @lego project: [ + // TODO: @lego - check if needed 'src/tsconfig.strict.json', ], } @@ -1517,10 +1511,6 @@ export default tseslint.config( { 'selector': 'interface', 'format': ['PascalCase'], 'prefix': ['I'] } ], 'comma-dangle': ['warn', 'only-multiline'], - // // // // TODO: @lego - // // // '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', - // // // TODO: @lego - comes from 'tseslint.configs.recommendedTypeChecked', but does not allow objects with `toString()` implementations - // // '@typescript-eslint/restrict-template-expressions': 'off', } }, ); diff --git a/src/vs/editor/common/codecs/utils/stream.ts b/src/vs/editor/common/codecs/utils/stream.ts index 6cb9f0ba62f..cf23382594c 100644 --- a/src/vs/editor/common/codecs/utils/stream.ts +++ b/src/vs/editor/common/codecs/utils/stream.ts @@ -3,26 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ITextModel } from '../../model.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; import { assertNever } from '../../../../base/common/assert.js'; import { ObservableDisposable } from '../../../../base/common/observableDisposable.js'; import { newWriteableStream, WriteableStream, ReadableStream } from '../../../../base/common/stream.js'; -/** - * TODO: @legomushroom - */ -export const arrayToGenerator = >(array: readonly T[]): Generator => { - return (function* (): Generator { - for (const item of array) { - yield item; - } - })(); -}; - /** * A readable stream of provided tokens. */ +// TODO: @legomushroom - add cancellation token support // TODO: @legomushroom - add unit tests export class Stream extends ObservableDisposable implements ReadableStream { + /** + * TODO: @legomushroom + */ + private ended: boolean = false; + /** * Underlying writable stream instance. */ @@ -34,25 +32,20 @@ export class Stream extends ObservableDisposable implements Re */ private interval: ReturnType | undefined; - // TODO: @legomushroom - private ended: boolean = false; - - /** - * TODO: @legomushroom - */ - public static fromArray( - array: readonly T[], - ): Stream { - return new Stream(arrayToGenerator(array)); - } - constructor( private readonly data: Generator, + private readonly cancellationToken?: CancellationToken, ) { super(); this.stream = newWriteableStream(null); + if (cancellationToken?.isCancellationRequested) { + this.end(); + + return; + } + // send couple of tokens immediately this.send(false); } @@ -70,9 +63,15 @@ export class Stream extends ObservableDisposable implements Re this.sendTokens() .then(() => { + if (this.cancellationToken?.isCancellationRequested) { + this.end(); + + return; + } + if (this.ended) { - this.stream.end(); - this.stopStream(); + this.end(); + return; } @@ -111,15 +110,13 @@ export class Stream extends ObservableDisposable implements Re tokensCount: number = 25, ): Promise { // send up to 'tokensCount' tokens at a time - // let token = this.data.read(); while (tokensCount > 0) { try { const token = this.data.next(); - if (token.done) { - this.ended = true; - this.stopStream(); - this.stream.end(); - break; + if (token.done || this.cancellationToken?.isCancellationRequested) { + this.end(); + + return; } await this.stream.write(token.value); @@ -134,6 +131,16 @@ export class Stream extends ObservableDisposable implements Re } } + /** + * TODO: @legomushroom + */ + private end(): this { + this.ended = true; + this.stream.end(); + this.stopStream(); + return this; + } + public pause(): void { this.stopStream(); this.stream.pause(); @@ -152,7 +159,7 @@ export class Stream extends ObservableDisposable implements Re this.dispose(); } - public removeListener(event: string, callback: Function): void { + public removeListener(event: string, callback: (...args: any[]) => void): void { this.stream.removeListener(event, callback); return; @@ -196,4 +203,63 @@ export class Stream extends ObservableDisposable implements Re super.dispose(); } + + /** + * TODO: @legomushroom + */ + public static fromArray( + array: T[], + cancellationToken?: CancellationToken, + ): Stream { + return new Stream(arrayToGenerator(array), cancellationToken); + } + + /** + * TODO: @legomushroom + */ + public static fromTextModel( + model: ITextModel, + cancellationToken?: CancellationToken, + ): Stream { + return new Stream(modelToGenerator(model), cancellationToken); + } + } + +/** + * TODO: @legomushroom + */ +export const arrayToGenerator = >(array: T[]): Generator => { + return (function* (): Generator { + for (const item of array) { + yield item; + } + })(); +}; + +/** + * TODO: @legomushroom + */ +export const modelToGenerator = (model: ITextModel): Generator => { + return (function* (): Generator { + const totalLines = model.getLineCount(); + let currentLine = 1; + + while (currentLine <= totalLines) { + if (model.isDisposed()) { + return undefined; + } + + yield VSBuffer.fromString( + model.getLineContent(currentLine), + ); + if (currentLine !== totalLines) { + yield VSBuffer.fromString( + model.getEOL(), + ); + } + + currentLine++; + } + })(); +}; diff --git a/src/vs/editor/test/common/codecs/testUtils/randomTokens.ts b/src/vs/editor/test/common/codecs/testUtils/randomTokens.ts new file mode 100644 index 00000000000..40f16703916 --- /dev/null +++ b/src/vs/editor/test/common/codecs/testUtils/randomTokens.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { Range } from '../../../../common/core/range.js'; +import { Text } from '../../../../common/codecs/textToken.js'; +import { randomInt } from '../../../../../base/common/numbers.js'; +import { assertNever } from '../../../../../base/common/assert.js'; +import { NewLine } from '../../../../common/codecs/linesCodec/tokens/newLine.js'; +import { Space, Word } from '../../../../common/codecs/simpleCodec/tokens/index.js'; + +/** + * Token type for the {@link cloneTokens} and {@link randomTokens} functions. + */ +type TToken = NewLine | Space | Word | Text; + +/** + * Test utility to clone a list of provided tokens. + */ +export const cloneTokens = ( + tokens: TToken[], +): TToken[] => { + const clonedTokens: TToken[] = []; + + for (const token of tokens) { + if (token instanceof NewLine) { + clonedTokens.push(new NewLine(token.range)); + continue; + } + + if (token instanceof Space) { + clonedTokens.push(new Space(token.range)); + continue; + } + + if (token instanceof Word) { + clonedTokens.push(new Word(token.range, token.text)); + + continue; + } + + if (token instanceof Text) { + clonedTokens.push(new Text(cloneTokens(token.tokens))); + continue; + } + + assertNever( + token, + `Unexpected token type '${token}'.`, + ); + } + + for (let i = 0; i < tokens.length; i++) { + assert( + tokens[i].equals(clonedTokens[i]), + `Original and cloned tokens #${i} must be equal.`, + ); + + assert( + tokens[i] !== clonedTokens[i], + `Original and cloned tokens #${i} must not be strict equal.`, + ); + } + + return clonedTokens; +}; + +/** + * Test utility to generate a number of random tokens. + */ +export const randomTokens = ( + tokenCount: number = randomInt(20, 10), + startLine: number = randomInt(100, 1), + startColumn: number = randomInt(100, 1), +): TToken[] => { + const tokens = []; + + let tokensLeft = tokenCount; + while (tokensLeft > 0) { + const caseNumber = randomInt(7, 1); + switch (caseNumber) { + case 1: + case 2: { + tokens.push( + new NewLine(new Range( + startLine, + startColumn, + startLine, + startColumn + 1, + )), + ); + startLine++; + startColumn = 1; + break; + } + case 3: + case 4: { + tokens.push( + new Space(new Range( + startLine, + startColumn, + startLine, + startColumn + 1, + )), + ); + startColumn++; + break; + } + + case 5: + case 6: { + const text = `word${randomInt(Number.MAX_SAFE_INTEGER, 1)}`; + const endColumn = startColumn + text.length; + + tokens.push( + new Word( + new Range( + startLine, startColumn, + startLine, endColumn, + ), + text, + ), + ); + + startColumn = endColumn; + break; + } + + case 7: { + const token = new Text( + randomTokens(randomInt(3, 1), startLine, startColumn), + ); + + tokens.push(token); + + startLine = token.range.endLineNumber; + startColumn = token.range.endColumn; + break; + } + + default: { + throw new Error(`Unexpected random token generation case number: '${caseNumber}'`); + } + } + + tokensLeft--; + } + + return tokens; +}; diff --git a/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts b/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts index 4d534d4a8ee..0092fe3d038 100644 --- a/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts +++ b/src/vs/editor/test/common/codecs/tokens/compositeToken.test.ts @@ -6,14 +6,12 @@ import assert from 'assert'; import { Range } from '../../../../common/core/range.js'; import { randomRange } from '../testUtils/randomRange.js'; -import { Text } from '../../../../common/codecs/textToken.js'; import { randomInt } from '../../../../../base/common/numbers.js'; import { BaseToken } from '../../../../common/codecs/baseToken.js'; -import { assertNever } from '../../../../../base/common/assert.js'; +import { cloneTokens, randomTokens } from '../testUtils/randomTokens.js'; import { CompositeToken } from '../../../../common/codecs/compositeToken.js'; import { randomBoolean } from '../../../../../base/test/common/testUtils.js'; -import { NewLine } from '../../../../common/codecs/linesCodec/tokens/newLine.js'; -import { Space, Word } from '../../../../common/codecs/simpleCodec/tokens/index.js'; +import { Word } from '../../../../common/codecs/simpleCodec/tokens/index.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('CompositeToken', () => { @@ -257,143 +255,3 @@ suite('CompositeToken', () => { }); }); }); - -/** - * Token type for the {@link cloneTokens} and {@link randomTokens} functions. - */ -type TToken = NewLine | Space | Word | Text; - -/** - * Test utility to clone a list of provided tokens. - */ -const cloneTokens = ( - tokens: TToken[], -): TToken[] => { - const clonedTokens: TToken[] = []; - - for (const token of tokens) { - if (token instanceof NewLine) { - clonedTokens.push(new NewLine(token.range)); - continue; - } - - if (token instanceof Space) { - clonedTokens.push(new Space(token.range)); - continue; - } - - if (token instanceof Word) { - clonedTokens.push(new Word(token.range, token.text)); - - continue; - } - - if (token instanceof Text) { - clonedTokens.push(new Text(cloneTokens(token.tokens))); - continue; - } - - assertNever( - token, - `Unexpected token type '${token}'.`, - ); - } - - for (let i = 0; i < tokens.length; i++) { - assert( - tokens[i].equals(clonedTokens[i]), - `Original and cloned tokens #${i} must be equal.`, - ); - - assert( - tokens[i] !== clonedTokens[i], - `Original and cloned tokens #${i} must not be strict equal.`, - ); - } - - return clonedTokens; -}; - -/** - * Test utility to generate a number of random tokens. - */ -const randomTokens = ( - tokenCount: number = randomInt(20, 10), - startLine: number = randomInt(100, 1), - startColumn: number = randomInt(100, 1), -): TToken[] => { - const tokens = []; - - let tokensLeft = tokenCount; - while (tokensLeft > 0) { - const caseNumber = randomInt(7, 1); - switch (caseNumber) { - case 1: - case 2: { - tokens.push( - new NewLine(new Range( - startLine, - startColumn, - startLine, - startColumn + 1, - )), - ); - startLine++; - startColumn = 1; - break; - } - case 3: - case 4: { - tokens.push( - new Space(new Range( - startLine, - startColumn, - startLine, - startColumn + 1, - )), - ); - startColumn++; - break; - } - - case 5: - case 6: { - const text = `word${randomInt(Number.MAX_SAFE_INTEGER, 1)}`; - const endColumn = startColumn + text.length; - - tokens.push( - new Word( - new Range( - startLine, startColumn, - startLine, endColumn, - ), - text, - ), - ); - - startColumn = endColumn; - break; - } - - case 7: { - const token = new Text( - randomTokens(randomInt(3, 1), startLine, startColumn), - ); - - tokens.push(token); - - startLine = token.range.endLineNumber; - startColumn = token.range.endColumn; - break; - } - - default: { - throw new Error(`Unexpected random token generation case number: '${caseNumber}'`); - } - } - - tokensLeft--; - } - - return tokens; -}; diff --git a/src/vs/platform/prompts/test/common/utils/mock.test.ts b/src/vs/platform/prompts/test/common/utils/mock.test.ts index 9b8113a8e5f..0539b73bde4 100644 --- a/src/vs/platform/prompts/test/common/utils/mock.test.ts +++ b/src/vs/platform/prompts/test/common/utils/mock.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { mockService } from './mock.js'; +import { mockObject, mockService } from './mock.js'; import { typeCheck } from '../../../../../base/common/types.js'; import { randomInt } from '../../../../../base/common/numbers.js'; import { randomBoolean } from '../../../../../base/test/common/testUtils.js'; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index 3ab8255a6a9..d2db780df08 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -16,33 +16,6 @@ import { IModelContentChangedEvent } from '../../../../../../editor/common/textM import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IPromptContentsProviderOptions, PromptContentsProviderBase } from './promptContentsProviderBase.js'; -/** - * TODO: @legomushroom - */ -export const modelToGenerator = (model: ITextModel): Generator => { - return (function* (): Generator { - const totalLines = model.getLineCount(); - let currentLine = 1; - - while (currentLine <= totalLines) { - if (model.isDisposed()) { - return undefined; - } - - yield VSBuffer.fromString( - model.getLineContent(currentLine), - ); - if (currentLine !== totalLines) { - yield VSBuffer.fromString( - model.getEOL(), - ); - } - - currentLine++; - } - })(); -}; - /** * Prompt contents provider for a {@link ITextModel} instance. */ @@ -66,6 +39,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase, @IInstantiationService private readonly initService: IInstantiationService, + // TODO: @legomushroom - use the log service? // @ILogService private readonly logService: ILogService, ) { super(options); @@ -89,7 +63,8 @@ export class TextModelContentsProvider extends PromptContentsProviderBase> { - return new Stream(modelToGenerator(this.model)); + // TODO: @legomushroom - do we need `IModelContentChangedEvent` here? + return Stream.fromTextModel(this.model, cancellationToken); } public override createNew( From 7212555ae4464032a445df0a1b88e9fb67852d54 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 10:14:24 -0700 Subject: [PATCH 48/90] add unit tests for the 'Stream' utility --- src/vs/editor/common/codecs/utils/stream.ts | 3 - .../test/common/codecs/utils/stream.test.ts | 163 ++++++++++++++++++ .../parsers/promptHeader/header.ts | 2 +- 3 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 src/vs/editor/test/common/codecs/utils/stream.test.ts diff --git a/src/vs/editor/common/codecs/utils/stream.ts b/src/vs/editor/common/codecs/utils/stream.ts index cf23382594c..2398bbc18b0 100644 --- a/src/vs/editor/common/codecs/utils/stream.ts +++ b/src/vs/editor/common/codecs/utils/stream.ts @@ -13,8 +13,6 @@ import { newWriteableStream, WriteableStream, ReadableStream } from '../../../.. /** * A readable stream of provided tokens. */ -// TODO: @legomushroom - add cancellation token support -// TODO: @legomushroom - add unit tests export class Stream extends ObservableDisposable implements ReadableStream { /** * TODO: @legomushroom @@ -223,7 +221,6 @@ export class Stream extends ObservableDisposable implements Re ): Stream { return new Stream(modelToGenerator(model), cancellationToken); } - } /** diff --git a/src/vs/editor/test/common/codecs/utils/stream.test.ts b/src/vs/editor/test/common/codecs/utils/stream.test.ts new file mode 100644 index 00000000000..758335f9637 --- /dev/null +++ b/src/vs/editor/test/common/codecs/utils/stream.test.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { URI } from '../../../../../base/common/uri.js'; +import { createTextModel } from '../../testTextModel.js'; +import { randomTokens } from '../testUtils/randomTokens.js'; +import { Stream } from '../../../../common/codecs/utils/stream.js'; +import { BaseToken } from '../../../../common/codecs/baseToken.js'; +import { assertDefined } from '../../../../../base/common/types.js'; +import { randomBoolean } from '../../../../../base/test/common/testUtils.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; +import { randomInt } from '../../../../../base/common/numbers.js'; + +suite('Stream', () => { + const disposables = ensureNoDisposablesAreLeakedInTestSuite(); + + suite('• fromArray()', () => { + test('• sends tokens in the array', async () => { + const tokens = randomTokens(); + + const stream = disposables.add(Stream.fromArray(tokens)); + const receivedTokens = await consume(stream); + + assertTokensEqual(receivedTokens, tokens); + }); + }); + + suite('• fromTextModel()', () => { + test('• sends data in text model', async () => { + const initialContents = [ + 'some contents', + 'with some line breaks', + 'and some more contents', + 'and even more contents', + ]; + + // both line endings should yield the same results + const lineEnding = (randomBoolean()) ? '\r\n' : '\n'; + + const model = disposables.add( + createTextModel( + initialContents.join(lineEnding), + 'unknown', + undefined, + URI.file('/foo.js'), + ), + ); + const stream = disposables.add(Stream.fromTextModel(model)); + + const receivedData = await consume(stream); + + assert.strictEqual( + receivedData.join(''), + initialContents.join(lineEnding), + 'Received data must be equal to the initial contents.', + ); + }); + }); + + suite('• cancellation token', () => { + test('• can be cancelled', async () => { + const initialContents = [ + 'some contents', + 'with some line breaks', + 'and some more contents', + 'and even more contents', + 'some contents', + 'with some line breaks', + 'and some more contents', + 'and even more contents', + ]; + + // both line endings should yield the same results + const lineEnding = (randomBoolean()) ? '\r\n' : '\n'; + + const model = disposables.add( + createTextModel( + initialContents.join(lineEnding), + 'unknown', + undefined, + URI.file('/foo.js'), + ), + ); + + const stopAtLine = randomInt(5, 2); + const cancellation = disposables.add(new CancellationTokenSource()); + + // override the `getLineContent` method to cancel the stream + // when a specific line number is being read from the model + const originalGetLineContent = model.getLineContent.bind(model); + model.getLineContent = (lineNumber: number) => { + // cancel the stream when we reach this specific line number + if (lineNumber === stopAtLine) { + cancellation.cancel(); + } + + return originalGetLineContent(lineNumber); + }; + + const stream = disposables.add( + Stream.fromTextModel(model, cancellation.token), + ); + + const receivedData = await consume(stream); + const expectedData = initialContents + .slice(0, stopAtLine - 1) + .join(lineEnding); + + assert.strictEqual( + receivedData.join(''), + // because the stream is cancelled before the last line, + // the last message always ends with the line ending + expectedData + lineEnding, + 'Received data must be equal to the contents before cancel.', + ); + }); + }); +}); + +/** + * TODO: @legomushroom + */ +const assertTokensEqual = ( + receivedTokens: BaseToken[], + expectedTokens: BaseToken[], +): void => { + for (let i = 0; i < expectedTokens.length; i++) { + const receivedToken = receivedTokens[i]; + + assertDefined( + receivedToken, + `Expected token #${i} to be '${expectedTokens[i]}', got 'undefined'.`, + ); + + assert.ok( + expectedTokens[i].equals(receivedTokens[i]), + `Expected token #${i} to be '${expectedTokens[i]}', got '${receivedToken}'.`, + ); + } +}; + +/** + * TODO: @legomushroom + */ +const consume = (stream: Stream): Promise => { + return new Promise((resolve, reject) => { + const receivedData: T[] = []; + stream.on('data', (token) => { + receivedData.push(token); + }); + + stream.on('end', () => { + resolve(receivedData); + }); + stream.on('error', (error) => { + reject(error); + }); + }); +}; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts index 5ac2e5a053a..bc6a77d5af7 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts @@ -94,7 +94,7 @@ export class PromptHeader extends Disposable { this.stream = this._register( new FrontMatterDecoder( - Stream.fromArray(contentsToken.tokens), + Stream.fromArray([...contentsToken.tokens]), ), ); this.stream.onData(this.onData.bind(this)); From 2f392d619e342dd098e5f903e785ef8ea40c1730 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 10:45:57 -0700 Subject: [PATCH 49/90] move strict tsconfig closer to the prompt files source code and add missing documentation comments --- eslint.config.js | 5 +- src/vs/editor/common/codecs/utils/stream.ts | 51 +++++++++---------- .../test/common/codecs/utils/stream.test.ts | 8 +-- .../platform/prompts}/tsconfig.strict.json | 4 +- .../textModelContentsProvider.ts | 3 -- .../promptSyntax/parsers/basePromptParser.ts | 4 +- 6 files changed, 34 insertions(+), 41 deletions(-) rename src/{ => vs/platform/prompts}/tsconfig.strict.json (92%) diff --git a/eslint.config.js b/eslint.config.js index 046a7a97cfb..0c100a1206a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1445,10 +1445,7 @@ export default tseslint.config( languageOptions: { parser: tseslint.parser, parserOptions: { - project: [ - // TODO: @lego - check if needed - 'src/tsconfig.strict.json', - ], + project: 'src/vs/platform/prompts/tsconfig.strict.json', } }, plugins: { diff --git a/src/vs/editor/common/codecs/utils/stream.ts b/src/vs/editor/common/codecs/utils/stream.ts index 2398bbc18b0..e5fad7a1fa8 100644 --- a/src/vs/editor/common/codecs/utils/stream.ts +++ b/src/vs/editor/common/codecs/utils/stream.ts @@ -5,8 +5,8 @@ import { ITextModel } from '../../model.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; +import { assert, assertNever } from '../../../../base/common/assert.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; -import { assertNever } from '../../../../base/common/assert.js'; import { ObservableDisposable } from '../../../../base/common/observableDisposable.js'; import { newWriteableStream, WriteableStream, ReadableStream } from '../../../../base/common/stream.js'; @@ -15,7 +15,7 @@ import { newWriteableStream, WriteableStream, ReadableStream } from '../../../.. */ export class Stream extends ObservableDisposable implements ReadableStream { /** - * TODO: @legomushroom + * Flag that indicates whether the stream has ended. */ private ended: boolean = false; @@ -40,24 +40,26 @@ export class Stream extends ObservableDisposable implements Re if (cancellationToken?.isCancellationRequested) { this.end(); - return; } - // send couple of tokens immediately - this.send(false); + // send a first batch of tokens immediately + this.send(true); } /** - * TODO: @legomushroom + * Starts process of sending tokens to the stream. + * + * @param stopAfterFirstSend whether to continue sending data to the stream or + * stop sending after the first batch of data is sent */ public send( - play: boolean = true, + stopAfterFirstSend: boolean = false, ): void { - // TODO: @legomushroom - throw instead? - if (this.ended) { - return; - } + assert( + this.ended === false, + 'Cannot send on already ended stream.', + ); this.sendTokens() .then(() => { @@ -73,17 +75,16 @@ export class Stream extends ObservableDisposable implements Re return; } - if (play === false) { + if (stopAfterFirstSend === true) { this.stopStream(); return; } this.interval = setImmediate(this.send.bind(this)); }) - .catch(() => { - this.stream.destroy(); - this.stream.end(); - this.stopStream(); + .catch((error) => { + this.stream.error(error); + this.dispose(); }); } @@ -119,18 +120,16 @@ export class Stream extends ObservableDisposable implements Re await this.stream.write(token.value); tokensCount--; - } catch { - this.stopStream(); - this.stream.destroy(); - // TODO: @legomushroom - needed? - this.stream.end(); + } catch (error) { + this.stream.error(error); + this.dispose(); return; } } } /** - * TODO: @legomushroom + * Ends the stream and stops sending tokens. */ private end(): this { this.ended = true; @@ -203,7 +202,7 @@ export class Stream extends ObservableDisposable implements Re } /** - * TODO: @legomushroom + * Create new instance of the stream from a provided array. */ public static fromArray( array: T[], @@ -213,7 +212,7 @@ export class Stream extends ObservableDisposable implements Re } /** - * TODO: @legomushroom + * Create new instance of the stream from a provided text model. */ public static fromTextModel( model: ITextModel, @@ -224,7 +223,7 @@ export class Stream extends ObservableDisposable implements Re } /** - * TODO: @legomushroom + * Create a generator out of a provided array. */ export const arrayToGenerator = >(array: T[]): Generator => { return (function* (): Generator { @@ -235,7 +234,7 @@ export const arrayToGenerator = >(array: T[]): Ge }; /** - * TODO: @legomushroom + * Create a generator out of a provided text model. */ export const modelToGenerator = (model: ITextModel): Generator => { return (function* (): Generator { diff --git a/src/vs/editor/test/common/codecs/utils/stream.test.ts b/src/vs/editor/test/common/codecs/utils/stream.test.ts index 758335f9637..cb6821b6684 100644 --- a/src/vs/editor/test/common/codecs/utils/stream.test.ts +++ b/src/vs/editor/test/common/codecs/utils/stream.test.ts @@ -7,13 +7,13 @@ import * as assert from 'assert'; import { URI } from '../../../../../base/common/uri.js'; import { createTextModel } from '../../testTextModel.js'; import { randomTokens } from '../testUtils/randomTokens.js'; +import { randomInt } from '../../../../../base/common/numbers.js'; import { Stream } from '../../../../common/codecs/utils/stream.js'; import { BaseToken } from '../../../../common/codecs/baseToken.js'; import { assertDefined } from '../../../../../base/common/types.js'; import { randomBoolean } from '../../../../../base/test/common/testUtils.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; -import { randomInt } from '../../../../../base/common/numbers.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('Stream', () => { const disposables = ensureNoDisposablesAreLeakedInTestSuite(); @@ -122,7 +122,7 @@ suite('Stream', () => { }); /** - * TODO: @legomushroom + * Asserts that two tokens lists are equal. */ const assertTokensEqual = ( receivedTokens: BaseToken[], @@ -144,7 +144,7 @@ const assertTokensEqual = ( }; /** - * TODO: @legomushroom + * Consume a provided stream and return a list of received data objects. */ const consume = (stream: Stream): Promise => { return new Promise((resolve, reject) => { diff --git a/src/tsconfig.strict.json b/src/vs/platform/prompts/tsconfig.strict.json similarity index 92% rename from src/tsconfig.strict.json rename to src/vs/platform/prompts/tsconfig.strict.json index c4b8970aca0..27429134795 100644 --- a/src/tsconfig.strict.json +++ b/src/vs/platform/prompts/tsconfig.strict.json @@ -1,6 +1,7 @@ { - "extends": "./tsconfig.json", + "extends": "../../../tsconfig.json", "compilerOptions": { + "strict": true, "allowUnusedLabels": false, "allowUnreachableCode": false, "alwaysStrict": true, @@ -14,6 +15,5 @@ "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, - "strict": true, } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index d2db780df08..7a6cb0d54d6 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -39,8 +39,6 @@ export class TextModelContentsProvider extends PromptContentsProviderBase, @IInstantiationService private readonly initService: IInstantiationService, - // TODO: @legomushroom - use the log service? - // @ILogService private readonly logService: ILogService, ) { super(options); @@ -63,7 +61,6 @@ export class TextModelContentsProvider extends PromptContentsProviderBase> { - // TODO: @legomushroom - do we need `IModelContentChangedEvent` here? return Stream.fromTextModel(this.model, cancellationToken); } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts index 42db59f8f44..0be808c1337 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts @@ -982,8 +982,8 @@ class FirstParseResult extends DeferredPromise { this._gotResult = true; super.complete(void 0) .catch(() => { - // noop - // TODO: @legomushroom + // the complete method is never fails + // so we can ignore the error here }); return; From c7a624a4be284064fb8d4d6355bad1d2213c6829 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 12:18:48 -0700 Subject: [PATCH 50/90] switch to `setTimeout` for the 'stream' utility to fix in-browser unit tests --- .../frontMatterCodec/frontMatterDecoder.ts | 2 +- src/vs/editor/common/codecs/utils/stream.ts | 18 ++++++++++++++---- .../test/common/codecs/utils/stream.test.ts | 18 +++++++++++++++++- .../parsers/textModelPromptParser.test.ts | 10 +++++----- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts b/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts index c24d6b424d9..afdecdd1801 100644 --- a/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts +++ b/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Stream } from '../utils/stream.js'; import { VALID_SPACE_TOKENS } from './constants.js'; import { Word } from '../simpleCodec/tokens/index.js'; -import { Stream } from '../utils/stream.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { ReadableStream } from '../../../../base/common/stream.js'; import { FrontMatterToken, FrontMatterRecord } from './tokens/index.js'; diff --git a/src/vs/editor/common/codecs/utils/stream.ts b/src/vs/editor/common/codecs/utils/stream.ts index e5fad7a1fa8..3ad2facb5e0 100644 --- a/src/vs/editor/common/codecs/utils/stream.ts +++ b/src/vs/editor/common/codecs/utils/stream.ts @@ -28,7 +28,7 @@ export class Stream extends ObservableDisposable implements Re * Interval reference that is used to periodically send * tokens to the stream in the background. */ - private interval: ReturnType | undefined; + private interval: ReturnType | undefined; constructor( private readonly data: Generator, @@ -56,6 +56,12 @@ export class Stream extends ObservableDisposable implements Re public send( stopAfterFirstSend: boolean = false, ): void { + if (this.cancellationToken?.isCancellationRequested) { + this.end(); + + return; + } + assert( this.ended === false, 'Cannot send on already ended stream.', @@ -80,7 +86,7 @@ export class Stream extends ObservableDisposable implements Re return; } - this.interval = setImmediate(this.send.bind(this)); + this.interval = setTimeout(this.send.bind(this)); }) .catch((error) => { this.stream.error(error); @@ -96,7 +102,7 @@ export class Stream extends ObservableDisposable implements Re return this; } - clearImmediate(this.interval); + clearTimeout(this.interval); delete this.interval; return this; @@ -132,9 +138,13 @@ export class Stream extends ObservableDisposable implements Re * Ends the stream and stops sending tokens. */ private end(): this { + if (this.ended) { + return this; + } this.ended = true; - this.stream.end(); + this.stopStream(); + this.stream.end(); return this; } diff --git a/src/vs/editor/test/common/codecs/utils/stream.test.ts b/src/vs/editor/test/common/codecs/utils/stream.test.ts index cb6821b6684..f6e9060abfc 100644 --- a/src/vs/editor/test/common/codecs/utils/stream.test.ts +++ b/src/vs/editor/test/common/codecs/utils/stream.test.ts @@ -8,11 +8,11 @@ import { URI } from '../../../../../base/common/uri.js'; import { createTextModel } from '../../testTextModel.js'; import { randomTokens } from '../testUtils/randomTokens.js'; import { randomInt } from '../../../../../base/common/numbers.js'; -import { Stream } from '../../../../common/codecs/utils/stream.js'; import { BaseToken } from '../../../../common/codecs/baseToken.js'; import { assertDefined } from '../../../../../base/common/types.js'; import { randomBoolean } from '../../../../../base/test/common/testUtils.js'; import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; +import { arrayToGenerator, Stream } from '../../../../common/codecs/utils/stream.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('Stream', () => { @@ -119,6 +119,22 @@ suite('Stream', () => { ); }); }); + + suite('• helpers', () => { + suite('• arrayToGenerator()', () => { + test('• sends tokens in the array', async () => { + const tokens = randomTokens(); + const generator = arrayToGenerator(tokens); + + const receivedTokens = []; + for (const token of generator) { + receivedTokens.push(token); + } + + assertTokensEqual(receivedTokens, tokens); + }); + }); + }); }); /** diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts index d5735551662..bcbd2f29f5f 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts @@ -18,8 +18,8 @@ import { randomBoolean } from '../../../../../../../base/test/common/testUtils.j import { FileService } from '../../../../../../../platform/files/common/fileService.js'; import { createTextModel } from '../../../../../../../editor/test/common/testTextModel.js'; import { ILogService, NullLogService } from '../../../../../../../platform/log/common/log.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../base/test/common/utils.js'; import { TextModelPromptParser } from '../../../../common/promptSyntax/parsers/textModelPromptParser.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../base/test/common/utils.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; import { INSTRUCTIONS_LANGUAGE_ID, PROMPT_LANGUAGE_ID } from '../../../../common/promptSyntax/constants.js'; import { InMemoryFileSystemProvider } from '../../../../../../../platform/files/common/inMemoryFilesystemProvider.js'; @@ -290,7 +290,7 @@ suite('TextModelPromptParser', () => { suite('• header', () => { suite(' • metadata', () => { - test('• has correct \'prompt\' metadata', async () => { + test(`• has correct 'prompt' metadata`, async () => { const test = createTest( URI.file('/absolute/folder/and/a/filename.txt'), [ @@ -333,7 +333,7 @@ suite('TextModelPromptParser', () => { assert.deepStrictEqual( tools, ['tool_name1', 'tool_name2'], - `Prompt header must have correct tools metadata.`, + `Prompt header must have correct tools metadata, got '${tools?.join(', ')}'.`, ); assert.strictEqual( @@ -355,7 +355,7 @@ suite('TextModelPromptParser', () => { ); }); - test('• has correct \'instructions\' metadata', async () => { + test(`• has correct 'instructions' metadata`, async () => { const test = createTest( URI.file('/absolute/folder/and/a/filename.instructions.md'), [ @@ -977,7 +977,7 @@ suite('TextModelPromptParser', () => { ); }); - test('• toString() implementation', async () => { + test('• toString()', async () => { const modelUri = URI.file('/Users/legomushroom/repos/prompt-snippets/README.md'); const test = createTest( modelUri, From d3e2fd99c15667bb2481f2d17e9873db8bae2040 Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Tue, 6 May 2025 12:30:13 -0700 Subject: [PATCH 51/90] rename 'Stream' class to 'ObjectStream' and improve documentation comments --- .../frontMatterCodec/frontMatterDecoder.ts | 6 +- .../utils/{stream.ts => objectStream.ts} | 56 +++++++++---------- .../{stream.test.ts => objectStream.test.ts} | 14 ++--- .../textModelContentsProvider.ts | 4 +- .../parsers/promptHeader/header.ts | 4 +- 5 files changed, 42 insertions(+), 42 deletions(-) rename src/vs/editor/common/codecs/utils/{stream.ts => objectStream.ts} (79%) rename src/vs/editor/test/common/codecs/utils/{stream.test.ts => objectStream.test.ts} (91%) diff --git a/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts b/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts index afdecdd1801..f7e95346a72 100644 --- a/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts +++ b/src/vs/editor/common/codecs/frontMatterCodec/frontMatterDecoder.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Stream } from '../utils/stream.js'; +import { ObjectStream } from '../utils/objectStream.js'; import { VALID_SPACE_TOKENS } from './constants.js'; import { Word } from '../simpleCodec/tokens/index.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; @@ -29,9 +29,9 @@ export class FrontMatterDecoder extends BaseDecoder | Stream, + stream: ReadableStream | ObjectStream, ) { - if (stream instanceof Stream) { + if (stream instanceof ObjectStream) { super(stream); return; diff --git a/src/vs/editor/common/codecs/utils/stream.ts b/src/vs/editor/common/codecs/utils/objectStream.ts similarity index 79% rename from src/vs/editor/common/codecs/utils/stream.ts rename to src/vs/editor/common/codecs/utils/objectStream.ts index 3ad2facb5e0..21183819921 100644 --- a/src/vs/editor/common/codecs/utils/stream.ts +++ b/src/vs/editor/common/codecs/utils/objectStream.ts @@ -11,9 +11,9 @@ import { ObservableDisposable } from '../../../../base/common/observableDisposab import { newWriteableStream, WriteableStream, ReadableStream } from '../../../../base/common/stream.js'; /** - * A readable stream of provided tokens. + * A readable stream of provided objects. */ -export class Stream extends ObservableDisposable implements ReadableStream { +export class ObjectStream extends ObservableDisposable implements ReadableStream { /** * Flag that indicates whether the stream has ended. */ @@ -26,9 +26,9 @@ export class Stream extends ObservableDisposable implements Re /** * Interval reference that is used to periodically send - * tokens to the stream in the background. + * objects to the stream in the background. */ - private interval: ReturnType | undefined; + private timeoutHandle: ReturnType | undefined; constructor( private readonly data: Generator, @@ -43,15 +43,15 @@ export class Stream extends ObservableDisposable implements Re return; } - // send a first batch of tokens immediately + // send a first batch of data immediately this.send(true); } /** - * Starts process of sending tokens to the stream. + * Starts process of sending data to the stream. * - * @param stopAfterFirstSend whether to continue sending data to the stream or - * stop sending after the first batch of data is sent + * @param stopAfterFirstSend whether to continue sending data to the stream + * or stop sending after the first batch of data is sent instead */ public send( stopAfterFirstSend: boolean = false, @@ -67,7 +67,7 @@ export class Stream extends ObservableDisposable implements Re 'Cannot send on already ended stream.', ); - this.sendTokens() + this.sendData() .then(() => { if (this.cancellationToken?.isCancellationRequested) { this.end(); @@ -86,7 +86,7 @@ export class Stream extends ObservableDisposable implements Re return; } - this.interval = setTimeout(this.send.bind(this)); + this.timeoutHandle = setTimeout(this.send.bind(this)); }) .catch((error) => { this.stream.error(error); @@ -95,37 +95,37 @@ export class Stream extends ObservableDisposable implements Re } /** - * Stop tokens sending interval. + * Stop the data sending loop. */ public stopStream(): this { - if (this.interval === undefined) { + if (this.timeoutHandle === undefined) { return this; } - clearTimeout(this.interval); - delete this.interval; + clearTimeout(this.timeoutHandle); + delete this.timeoutHandle; return this; } /** - * Sends a provided number of tokens to the stream. + * Sends a provided number of objects to the stream. */ - private async sendTokens( - tokensCount: number = 25, + private async sendData( + objectsCount: number = 25, ): Promise { - // send up to 'tokensCount' tokens at a time - while (tokensCount > 0) { + // send up to 'objectsCount' objects at a time + while (objectsCount > 0) { try { - const token = this.data.next(); - if (token.done || this.cancellationToken?.isCancellationRequested) { + const next = this.data.next(); + if (next.done || this.cancellationToken?.isCancellationRequested) { this.end(); return; } - await this.stream.write(token.value); - tokensCount--; + await this.stream.write(next.value); + objectsCount--; } catch (error) { this.stream.error(error); this.dispose(); @@ -135,7 +135,7 @@ export class Stream extends ObservableDisposable implements Re } /** - * Ends the stream and stops sending tokens. + * Ends the stream and stops sending data objects. */ private end(): this { if (this.ended) { @@ -217,8 +217,8 @@ export class Stream extends ObservableDisposable implements Re public static fromArray( array: T[], cancellationToken?: CancellationToken, - ): Stream { - return new Stream(arrayToGenerator(array), cancellationToken); + ): ObjectStream { + return new ObjectStream(arrayToGenerator(array), cancellationToken); } /** @@ -227,8 +227,8 @@ export class Stream extends ObservableDisposable implements Re public static fromTextModel( model: ITextModel, cancellationToken?: CancellationToken, - ): Stream { - return new Stream(modelToGenerator(model), cancellationToken); + ): ObjectStream { + return new ObjectStream(modelToGenerator(model), cancellationToken); } } diff --git a/src/vs/editor/test/common/codecs/utils/stream.test.ts b/src/vs/editor/test/common/codecs/utils/objectStream.test.ts similarity index 91% rename from src/vs/editor/test/common/codecs/utils/stream.test.ts rename to src/vs/editor/test/common/codecs/utils/objectStream.test.ts index f6e9060abfc..b9d2055c39b 100644 --- a/src/vs/editor/test/common/codecs/utils/stream.test.ts +++ b/src/vs/editor/test/common/codecs/utils/objectStream.test.ts @@ -12,17 +12,17 @@ import { BaseToken } from '../../../../common/codecs/baseToken.js'; import { assertDefined } from '../../../../../base/common/types.js'; import { randomBoolean } from '../../../../../base/test/common/testUtils.js'; import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; -import { arrayToGenerator, Stream } from '../../../../common/codecs/utils/stream.js'; +import { arrayToGenerator, ObjectStream } from '../../../../common/codecs/utils/objectStream.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -suite('Stream', () => { +suite('ObjectStream', () => { const disposables = ensureNoDisposablesAreLeakedInTestSuite(); suite('• fromArray()', () => { - test('• sends tokens in the array', async () => { + test('• sends objects in the array', async () => { const tokens = randomTokens(); - const stream = disposables.add(Stream.fromArray(tokens)); + const stream = disposables.add(ObjectStream.fromArray(tokens)); const receivedTokens = await consume(stream); assertTokensEqual(receivedTokens, tokens); @@ -49,7 +49,7 @@ suite('Stream', () => { URI.file('/foo.js'), ), ); - const stream = disposables.add(Stream.fromTextModel(model)); + const stream = disposables.add(ObjectStream.fromTextModel(model)); const receivedData = await consume(stream); @@ -102,7 +102,7 @@ suite('Stream', () => { }; const stream = disposables.add( - Stream.fromTextModel(model, cancellation.token), + ObjectStream.fromTextModel(model, cancellation.token), ); const receivedData = await consume(stream); @@ -162,7 +162,7 @@ const assertTokensEqual = ( /** * Consume a provided stream and return a list of received data objects. */ -const consume = (stream: Stream): Promise => { +const consume = (stream: ObjectStream): Promise => { return new Promise((resolve, reject) => { const receivedData: T[] = []; stream.on('data', (token) => { diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index 7a6cb0d54d6..0a109a8d97d 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -10,8 +10,8 @@ import { ITextModel } from '../../../../../../editor/common/model.js'; import { ReadableStream } from '../../../../../../base/common/stream.js'; import { FilePromptContentProvider } from './filePromptContentsProvider.js'; import { TextModel } from '../../../../../../editor/common/model/textModel.js'; -import { Stream } from '../../../../../../editor/common/codecs/utils/stream.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; +import { ObjectStream } from '../../../../../../editor/common/codecs/utils/objectStream.js'; import { IModelContentChangedEvent } from '../../../../../../editor/common/textModelEvents.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IPromptContentsProviderOptions, PromptContentsProviderBase } from './promptContentsProviderBase.js'; @@ -61,7 +61,7 @@ export class TextModelContentsProvider extends PromptContentsProviderBase> { - return Stream.fromTextModel(this.model, cancellationToken); + return ObjectStream.fromTextModel(this.model, cancellationToken); } public override createNew( diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts index bc6a77d5af7..30b0aaa2ea6 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/promptHeader/header.ts @@ -11,7 +11,7 @@ import { assertDefined } from '../../../../../../../base/common/types.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { Text } from '../../../../../../../editor/common/codecs/textToken.js'; import { PromptMetadataError, PromptMetadataWarning, TDiagnostic } from './diagnostics.js'; -import { Stream } from '../../../../../../../editor/common/codecs/utils/stream.js'; +import { ObjectStream } from '../../../../../../../editor/common/codecs/utils/objectStream.js'; import { SimpleToken } from '../../../../../../../editor/common/codecs/simpleCodec/tokens/index.js'; import { PromptToolsMetadata, PromptModeMetadata, PromptDescriptionMetadata } from './metadata/index.js'; import { FrontMatterRecord } from '../../../../../../../editor/common/codecs/frontMatterCodec/tokens/index.js'; @@ -94,7 +94,7 @@ export class PromptHeader extends Disposable { this.stream = this._register( new FrontMatterDecoder( - Stream.fromArray([...contentsToken.tokens]), + ObjectStream.fromArray([...contentsToken.tokens]), ), ); this.stream.onData(this.onData.bind(this)); From 9cb8bbcd49be4cb9fc37d4ad5305dabe79cd5727 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 6 May 2025 15:28:04 -0700 Subject: [PATCH 52/90] Convert service-worker.js to ts Fixes #229879 --- build/buildfile.js | 3 +- .../{service-worker.js => service-worker.ts} | 179 +++++++----------- 2 files changed, 69 insertions(+), 113 deletions(-) rename src/vs/workbench/contrib/webview/browser/pre/{service-worker.js => service-worker.ts} (76%) diff --git a/build/buildfile.js b/build/buildfile.js index 9430fb3d7be..614d5f9cdad 100644 --- a/build/buildfile.js +++ b/build/buildfile.js @@ -45,7 +45,8 @@ exports.code = [ createModuleDescription('vs/code/electron-utility/sharedProcess/sharedProcessMain'), createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain'), createModuleDescription('vs/code/electron-sandbox/workbench/workbench'), - createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorer') + createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorer'), + createModuleDescription('vs/workbench/contrib/webview/browser/pre/service-worker') ]; exports.codeWeb = createModuleDescription('vs/code/browser/workbench/workbench'); diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.ts similarity index 76% rename from src/vs/workbench/contrib/webview/browser/pre/service-worker.js rename to src/vs/workbench/contrib/webview/browser/pre/service-worker.ts index 3d74103ec24..ab6f674be34 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.ts @@ -2,11 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// @ts-check - /// -const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {any} */ (self)); +const sw: ServiceWorkerGlobalScope = self as any as ServiceWorkerGlobalScope; const VERSION = 4; @@ -27,46 +25,29 @@ const resourceBaseAuthority = searchParams.get('vscode-resource-base-authority') const resolveTimeout = 30_000; -/** - * @template T - * @typedef {{ status: 'ok'; value: T } | { status: 'timeout' }} RequestStoreResult - */ +type RequestStoreResult = { + status: 'ok'; + value: T; +} | { + status: 'timeout'; +}; -/** - * @template T - * @typedef {{ - * resolve: (x: RequestStoreResult) => void, - * promise: Promise> - * }} RequestStoreEntry - */ +interface RequestStoreEntry { + resolve: (x: RequestStoreResult) => void; + promise: Promise>; +} -/** - * Caches - * @template T - */ -class RequestStore { - constructor() { - /** @type {Map>} */ - this.map = new Map(); +class RequestStore { + private map: Map> = new Map(); + private requestPool: number = 0; - this.requestPool = 0; - } - - /** - * @returns {{ requestId: number, promise: Promise> }} - */ - create() { + create(): { requestId: number; promise: Promise> } { const requestId = ++this.requestPool; - /** @type {undefined | ((x: RequestStoreResult) => void)} */ - let resolve; - - /** @type {Promise>} */ - const promise = new Promise(r => resolve = r); - - /** @type {RequestStoreEntry} */ - const entry = { resolve: /** @type {(x: RequestStoreResult) => void} */ (resolve), promise }; + let resolve: (x: RequestStoreResult) => void; + const promise = new Promise>(r => resolve = r); + const entry: RequestStoreEntry = { resolve: resolve!, promise }; this.map.set(requestId, entry); const dispose = () => { @@ -75,19 +56,13 @@ class RequestStore { if (existingEntry === entry) { existingEntry.resolve({ status: 'timeout' }); this.map.delete(requestId); - return; } }; const timeout = setTimeout(dispose, resolveTimeout); return { requestId, promise }; } - /** - * @param {number} requestId - * @param {T} result - * @return {boolean} - */ - resolve(requestId, result) { + resolve(requestId: number, result: T): boolean { const entry = this.map.get(requestId); if (!entry) { return false; @@ -98,26 +73,15 @@ class RequestStore { } } -/** - * @typedef {{ readonly status: 200; id: number; path: string; mime: string; data: Uint8Array; etag: string | undefined; mtime: number | undefined; } - * | { readonly status: 304; id: number; path: string; mime: string; mtime: number | undefined } - * | { readonly status: 401; id: number; path: string } - * | { readonly status: 404; id: number; path: string }} ResourceResponse - */ - /** * Map of requested paths to responses. - * - * @type {RequestStore} */ -const resourceRequestStore = new RequestStore(); +const resourceRequestStore = new RequestStore(); /** * Map of requested localhost origins to optional redirects. - * - * @type {RequestStore} */ -const localhostRequestStore = new RequestStore(); +const localhostRequestStore = new RequestStore(); const unauthorized = () => new Response('Unauthorized', { status: 401, }); @@ -131,10 +95,14 @@ const methodNotAllowed = () => const requestTimeout = () => new Response('Request Timeout', { status: 408, }); -sw.addEventListener('message', async (event) => { +sw.addEventListener('message', async (event: ExtendableMessageEvent) => { + if (!event.source) { + return; + } + + const source = event.source as Client; switch (event.data.channel) { case 'version': { - const source = /** @type {Client} */ (event.source); sw.clients.get(source.id).then(client => { if (client) { client.postMessage({ @@ -146,8 +114,7 @@ sw.addEventListener('message', async (event) => { return; } case 'did-load-resource': { - /** @type {ResourceResponse} */ - const response = event.data.data; + const response = event.data.data as ResourceResponse; if (!resourceRequestStore.resolve(response.id, response)) { console.log('Could not resolve unknown resource', response.path); } @@ -167,7 +134,7 @@ sw.addEventListener('message', async (event) => { } }); -sw.addEventListener('fetch', (event) => { +sw.addEventListener('fetch', (event: FetchEvent) => { const requestUrl = new URL(event.request.url); if (typeof resourceBaseAuthority === 'string' && requestUrl.protocol === 'https:' && requestUrl.hostname.endsWith('.' + resourceBaseAuthority)) { switch (event.request.method) { @@ -216,26 +183,27 @@ sw.addEventListener('fetch', (event) => { } }); -sw.addEventListener('install', (event) => { +sw.addEventListener('install', (event: ExtendableEvent) => { event.waitUntil(sw.skipWaiting()); // Activate worker immediately }); -sw.addEventListener('activate', (event) => { +sw.addEventListener('activate', (event: ExtendableEvent) => { event.waitUntil(sw.clients.claim()); // Become available to all pages }); -/** - * @param {FetchEvent} event - * @param {{ - * scheme: string; - * authority: string; - * path: string; - * query: string; - * }} requestUrlComponents - */ -async function processResourceRequest(event, requestUrlComponents) { +interface ResourceRequestUrlComponents { + scheme: string; + authority: string; + path: string; + query: string; +} + +async function processResourceRequest( + event: FetchEvent, + requestUrlComponents: ResourceRequestUrlComponents +): Promise { const client = await sw.clients.get(event.clientId); - let webviewId; + let webviewId: string | null | undefined; if (!client) { const workerClient = await getWorkerClientForId(event.clientId); if (!workerClient) { @@ -255,11 +223,10 @@ async function processResourceRequest(event, requestUrlComponents) { const shouldTryCaching = (event.request.method === 'GET'); - /** - * @param {RequestStoreResult} result - * @param {Response | undefined} cachedResponse - */ - const resolveResourceEntry = (result, cachedResponse) => { + const resolveResourceEntry = ( + result: RequestStoreResult, + cachedResponse: Response | undefined + ): Response => { if (result.status === 'timeout') { return requestTimeout(); } @@ -281,8 +248,7 @@ async function processResourceRequest(event, requestUrlComponents) { return notFound(); } - /** @type {Record} */ - const commonHeaders = { + const commonHeaders: Record = { 'Access-Control-Allow-Origin': '*', }; @@ -317,8 +283,7 @@ async function processResourceRequest(event, requestUrlComponents) { } } - /** @type {Record} */ - const headers = { + const headers: Record = { ...commonHeaders, 'Content-Type': entry.mime, 'Content-Length': byteLength.toString(), @@ -362,8 +327,7 @@ async function processResourceRequest(event, requestUrlComponents) { return notFound(); } - /** @type {Response | undefined} */ - let cached; + let cached: Response | undefined; if (shouldTryCaching) { const cache = await caches.open(resourceCacheName); cached = await cache.match(event.request); @@ -386,12 +350,10 @@ async function processResourceRequest(event, requestUrlComponents) { return promise.then(entry => resolveResourceEntry(entry, cached)); } -/** - * @param {FetchEvent} event - * @param {URL} requestUrl - * @return {Promise} - */ -async function processLocalhostRequest(event, requestUrl) { +async function processLocalhostRequest( + event: FetchEvent, + requestUrl: URL +): Promise { const client = await sw.clients.get(event.clientId); if (!client) { // This is expected when requesting resources on other localhost ports @@ -406,11 +368,9 @@ async function processLocalhostRequest(event, requestUrl) { const origin = requestUrl.origin; - /** - * @param {RequestStoreResult} result - * @return {Promise} - */ - const resolveRedirect = async (result) => { + const resolveRedirect = async ( + result: RequestStoreResult + ): Promise => { if (result.status !== 'ok' || !result.value) { return fetch(event.request); } @@ -443,11 +403,7 @@ async function processLocalhostRequest(event, requestUrl) { return promise.then(resolveRedirect); } -/** - * @param {Client} client - * @returns {string | null} - */ -function getWebviewIdForClient(client) { +function getWebviewIdForClient(client: Client): string | null { // Refs https://github.com/microsoft/vscode/issues/244143 // With PlzDedicatedWorker, worker subresources and blob wokers // will use clients different from the window client. @@ -461,11 +417,7 @@ function getWebviewIdForClient(client) { return requesterClientUrl.searchParams.get('id'); } -/** - * @param {string} webviewId - * @returns {Promise} - */ -async function getOuterIframeClient(webviewId) { +async function getOuterIframeClient(webviewId: string): Promise { const allClients = await sw.clients.matchAll({ includeUncontrolled: true }); return allClients.filter(client => { const clientUrl = new URL(client.url); @@ -474,11 +426,7 @@ async function getOuterIframeClient(webviewId) { }); } -/** - * @param {string} clientId - * @returns {Promise} - */ -async function getWorkerClientForId(clientId) { +async function getWorkerClientForId(clientId: string): Promise { const allDedicatedWorkerClients = await sw.clients.matchAll({ type: 'worker' }); const allSharedWorkerClients = await sw.clients.matchAll({ type: 'sharedworker' }); const allWorkerClients = [...allDedicatedWorkerClients, ...allSharedWorkerClients]; @@ -486,3 +434,10 @@ async function getWorkerClientForId(clientId) { return client.id === clientId; }); } + +type ResourceResponse = + | { readonly status: 200; id: number; path: string; mime: string; data: Uint8Array; etag: string | undefined; mtime: number | undefined } + | { readonly status: 304; id: number; path: string; mime: string; mtime: number | undefined } + | { readonly status: 401; id: number; path: string } + | { readonly status: 404; id: number; path: string } + ; From 690d2be2588613c893a0f8bf7702e1f5bdb7a4ea Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt <2644648+TylerLeonhardt@users.noreply.github.com> Date: Tue, 6 May 2025 15:36:17 -0700 Subject: [PATCH 53/90] Clean up some dead code and add a telemetry even to track classic microsoft auth usage (#248256) So we can see how many people disable MSAL. --- .../src/common/telemetryReporter.ts | 7 +++++++ extensions/microsoft-authentication/src/extension.ts | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/extensions/microsoft-authentication/src/common/telemetryReporter.ts b/extensions/microsoft-authentication/src/common/telemetryReporter.ts index c28aa887e0c..e18c743de9b 100644 --- a/extensions/microsoft-authentication/src/common/telemetryReporter.ts +++ b/extensions/microsoft-authentication/src/common/telemetryReporter.ts @@ -35,6 +35,13 @@ export class MicrosoftAuthenticationTelemetryReporter implements IExperimentatio ); } + sendActivatedWithClassicImplementationEvent(): void { + /* __GDPR__ + "activatingClassic" : { "owner": "TylerLeonhardt", "comment": "Used to determine how often users use the classic login flow." } + */ + this._telemetryReporter.sendTelemetryEvent('activatingClassic'); + } + sendLoginEvent(scopes: readonly string[]): void { /* __GDPR__ "login" : { diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index bd32a82290a..6a5fac9765d 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -48,14 +48,12 @@ export async function activate(context: ExtensionContext) { env.uriScheme !== 'vscode', // isPreRelease ); useMsal = shouldUseMsal(expService); - const clientIdVersion = workspace.getConfiguration('microsoft-authentication').get<'v1' | 'v2'>('clientIdVersion', 'v1'); - context.subscriptions.push(workspace.onDidChangeConfiguration(async e => { if (!e.affectsConfiguration('microsoft-authentication')) { return; } - if (useMsal === shouldUseMsal(expService) && clientIdVersion === workspace.getConfiguration('microsoft-authentication').get<'v1' | 'v2'>('clientIdVersion', 'v1')) { + if (useMsal === shouldUseMsal(expService)) { return; } @@ -77,6 +75,7 @@ export async function activate(context: ExtensionContext) { if (useMsal && typeof navigator === 'undefined') { await extensionV2.activate(context, mainTelemetryReporter); } else { + mainTelemetryReporter.sendActivatedWithClassicImplementationEvent(); await extensionV1.activate(context, mainTelemetryReporter.telemetryReporter); } } From 76c064bd8794996f12b3de5a2da9e6f42d05b11f Mon Sep 17 00:00:00 2001 From: bhack Date: Wed, 7 May 2025 03:33:19 +0400 Subject: [PATCH 54/90] Add to new source format and the mandatory signed-by (#239390) fixes #238697 --- resources/linux/debian/postinst.template | 28 ++++++++++++++++++++---- resources/linux/debian/postrm.template | 5 ++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index b44aa361cb3..9d36d52df54 100755 --- a/resources/linux/debian/postinst.template +++ b/resources/linux/debian/postinst.template @@ -33,8 +33,8 @@ if [ "@@NAME@@" != "code-oss" ]; then CODE_SOURCE_PART=${APT_SOURCE_PARTS}vscode.list CODE_SOURCE_PART_DEB822=${APT_SOURCE_PARTS}vscode.sources - eval $(apt-config shell APT_TRUSTED_PARTS Dir::Etc::trustedparts/d) - CODE_TRUSTED_PART=${APT_TRUSTED_PARTS}microsoft.gpg + CODE_TRUSTED_PART=/usr/share/keyrings/microsoft.gpg + CODE_TRUSTED_PART_OLD="/etc/apt/trusted.gpg.d/microsoft.gpg" # RET seems to be true by default even after db_get is called on a first install. RET='true' @@ -48,7 +48,7 @@ if [ "@@NAME@@" != "code-oss" ]; then if [ "$RET" = 'false' ]; then # The user specified in debconf not to add the Microsoft repository WRITE_SOURCE='no' - elif [ -f "$CODE_SOURCE_PART_DEB822" ]; then + elif [ -f "$CODE_SOURCE_PART_DEB822" ] && [ -f "$CODE_TRUSTED_PART" ]; then # The user has migrated themselves to the DEB822 format WRITE_SOURCE='no' elif [ -f "$CODE_SOURCE_PART" ] && (grep -q "http://packages.microsoft.com/repos/vscode" $CODE_SOURCE_PART); then @@ -81,6 +81,8 @@ if [ "@@NAME@@" != "code-oss" ]; then db_get @@NAME@@/add-microsoft-repo if [ "$RET" = false ]; then WRITE_SOURCE='no' + else + WRITE_SOURCE='yes' fi else # The terminal is interactive but there is no debconf. @@ -90,9 +92,24 @@ if [ "@@NAME@@" != "code-oss" ]; then fi if [ "$WRITE_SOURCE" != 'no' ]; then - echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### + # Check if apt modernize-sources is available. + if apt modernize-sources --help >/dev/null 2>&1; then + # Write repository in deb822 format with Signed-By. + echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### +# You may comment out this entry, but any other modifications may be lost." > "$CODE_SOURCE_PART_DEB822" + cat <> "$CODE_SOURCE_PART_DEB822" +Types: deb +URIs: https://packages.microsoft.com/repos/code +Suites: stable +Components: main +Architectures: amd64,arm64,armhf +Signed-By: $CODE_TRUSTED_PART +EOF + else + echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### # You may comment out this entry, but any other modifications may be lost. deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/repos/code stable main" > $CODE_SOURCE_PART + fi # Sourced from https://packages.microsoft.com/keys/microsoft.asc if [ ! -f $CODE_TRUSTED_PART ]; then @@ -116,6 +133,9 @@ NdCFTW7wY0Fb1fWJ+/KTsC4= =J6gs -----END PGP PUBLIC KEY BLOCK----- " | gpg --dearmor > $CODE_TRUSTED_PART + if [ -f "$CODE_TRUSTED_PART_OLD" ]; then + rm -f "$CODE_TRUSTED_PART_OLD" + fi fi fi fi diff --git a/resources/linux/debian/postrm.template b/resources/linux/debian/postrm.template index dcbfda95ea0..d9cfd495d6b 100755 --- a/resources/linux/debian/postrm.template +++ b/resources/linux/debian/postrm.template @@ -22,11 +22,10 @@ if [ -e '/usr/share/debconf/confmodule' ]; then fi if [ "$RET" = "true" ]; then eval $(apt-config shell APT_SOURCE_PARTS Dir::Etc::sourceparts/d) - CODE_SOURCE_PART=${APT_SOURCE_PARTS}vscode.list + CODE_SOURCE_PART=${APT_SOURCE_PARTS}vscode.sources rm -f $CODE_SOURCE_PART - eval $(apt-config shell APT_TRUSTED_PARTS Dir::Etc::trustedparts/d) - CODE_TRUSTED_PART=${APT_TRUSTED_PARTS}microsoft.gpg + CODE_TRUSTED_PART=/usr/share/keyrings/microsoft.gpg rm -f $CODE_TRUSTED_PART fi From 225f587e1e5da35cfdd894dd68d54fd6fb160715 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 6 May 2025 17:15:35 -0700 Subject: [PATCH 55/90] Add nicer tooltip to mode picker (#248267) Fix microsoft/vscode-copilot#16666 --- .../contrib/chat/browser/modelPicker/modePickerActionItem.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/modelPicker/modePickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/modelPicker/modePickerActionItem.ts index e6d7e88874c..4b4d7d9f494 100644 --- a/src/vs/workbench/contrib/chat/browser/modelPicker/modePickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/modelPicker/modePickerActionItem.ts @@ -15,7 +15,7 @@ import { IActionWidgetDropdownActionProvider, IActionWidgetDropdownOptions } fro import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { IChatAgentService } from '../../common/chatAgents.js'; -import { ChatMode, modeToString } from '../../common/constants.js'; +import { ChatAgentLocation, ChatMode, modeToString } from '../../common/constants.js'; import { getOpenChatActionIdForMode } from '../actions/chatActions.js'; import { IToggleChatModeArgs } from '../actions/chatExecuteActions.js'; @@ -40,6 +40,7 @@ export class ModePickerActionItem extends ActionWidgetDropdownActionViewItem { class: undefined, enabled: true, checked: delegate.getMode() === mode, + tooltip: chatAgentService.getDefaultAgent(ChatAgentLocation.Panel, mode)?.description ?? action.tooltip, run: async () => { const result = await action.run({ mode } satisfies IToggleChatModeArgs); this.renderLabel(this.element!); From 4437fa4ae2ce931d90421d8cae4578b48b7ddfcb Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Tue, 6 May 2025 17:57:32 -0700 Subject: [PATCH 56/90] Update getting-started actions and message (#248269) --- .../welcomeGettingStarted/browser/gettingStarted.ts | 8 ++++++-- .../browser/gettingStartedExpService.ts | 4 +--- .../browser/media/gettingStarted.css | 8 +++++++- .../welcomeGettingStarted/common/gettingStartedContent.ts | 4 +++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index fc9b455003e..e33143a6112 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -18,7 +18,7 @@ import { onUnexpectedError } from '../../../../base/common/errors.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { splitRecentLabel } from '../../../../base/common/labels.js'; import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; -import { ILink, LinkedText } from '../../../../base/common/linkedText.js'; +import { ILink, LinkedText, parseLinkedText } from '../../../../base/common/linkedText.js'; import { parse } from '../../../../base/common/marshalling.js'; import { Schemas, matchesScheme } from '../../../../base/common/network.js'; import { isMacintosh, OS } from '../../../../base/common/platform.js'; @@ -63,7 +63,7 @@ import { gettingStartedCheckedCodicon, gettingStartedUncheckedCodicon } from './ import { GettingStartedEditorOptions, GettingStartedInput } from './gettingStartedInput.js'; import { IResolvedWalkthrough, IResolvedWalkthroughStep, IWalkthroughsService, hiddenEntriesConfigurationKey, parseDescription } from './gettingStartedService.js'; import { RestoreWalkthroughsConfigurationValue, restoreWalkthroughsConfigurationKey } from './startupPage.js'; -import { NEW_WELCOME_EXPERIENCE, startEntries } from '../common/gettingStartedContent.js'; +import { copilotSettingsMessage, NEW_WELCOME_EXPERIENCE, startEntries } from '../common/gettingStartedContent.js'; import { GroupDirection, GroupsOrder, IEditorGroup, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IHostService } from '../../../services/host/browser/host.js'; @@ -1616,6 +1616,10 @@ export class GettingStartedPage extends EditorPane { const descElement = $('.multi-step-action'); this.buildMarkdownDescription(descElement, [linkedText]); multiStepContainer.appendChild(descElement); + const actionMessage = $('span.action-message'); + const updatedText = parseLinkedText(copilotSettingsMessage); + this.buildMarkdownDescription(actionMessage, [updatedText]); + multiStepContainer.appendChild(actionMessage); } textContent.appendChild(multiStepContainer); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExpService.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExpService.ts index f3706e2a844..ad7d61fcf0e 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExpService.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExpService.ts @@ -42,9 +42,7 @@ export enum GettingStartedExperimentGroup { const STABLE_EXPERIMENT_GROUPS: ExperimentGroupDefinition[] = [ // Bump the iteration each time we change group allocations - { name: GettingStartedExperimentGroup.New, min: 0.0, max: 0.1, iteration: 1, walkthroughId: 'NewWelcomeExperience' }, - { name: GettingStartedExperimentGroup.Control, min: 0.1, max: 0.2, iteration: 1, walkthroughId: 'Setup' }, - { name: GettingStartedExperimentGroup.Default, min: 0.2, max: 1, iteration: 1, walkthroughId: 'Setup' } + { name: GettingStartedExperimentGroup.Default, min: 0, max: 1, iteration: 1, walkthroughId: 'Setup' } ]; const INSIDERS_EXPERIMENT_GROUPS: ExperimentGroupDefinition[] = [ diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css index ad6d0335a67..ef5e3d76bb2 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css @@ -1404,7 +1404,6 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer.newSlide .multi-step-container .button-container { margin-top: 2rem; - margin-left: 16px } .monaco-workbench .part.editor > .content .gettingStartedContainer.newSlide.width-constrained .multi-step-container .button-container, @@ -1412,6 +1411,13 @@ margin-top: 1rem; } +.monaco-workbench .part.editor > .content .gettingStartedContainer.newSlide .multi-step-container .action-message{ + display: flex; + color: var(--vscode-descriptionForeground); + font-size: 12px; + margin-top: 12px; +} + .monaco-workbench .part.editor > .content .gettingStartedContainer.newSlide.width-constrained .sub-step-title { padding-top: 2px; } diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 949cea754cc..72b6da5f26a 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -19,6 +19,8 @@ interface IGettingStartedContentProvider { (): string; } +export const copilotSettingsMessage = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot Free and Pro may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", product.defaultChatAgent?.publicCodeMatchesUrl, product.defaultChatAgent?.manageSettingsUrl); + class GettingStartedContentProviderRegistry { private readonly providers = new Map(); @@ -698,7 +700,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ { id: 'copilotSetup.customize', title: localize('gettingStarted.customize.title', "Personalized to how you work"), - description: localize('gettingStarted.customize.description', "Swap models, add agent mode tools, and create personalized instructions.\n{0}", Button(localize('signUp', "Set up AI"), 'command:workbench.action.chat.triggerSetup')), + description: localize('gettingStarted.customize.description', "Swap models, add agent mode tools, and create personalized instructions.\n{0}", Button(localize('signUp', "Set up AI"), 'command:workbench.action.chat.triggerSetupWithoutDialog')), media: { type: 'svg', altText: 'Personalize', path: 'multi-file-edits.svg' }, From f229e7c3e66b5592add46382839c07ba74a5e609 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 6 May 2025 18:53:35 -0700 Subject: [PATCH 57/90] Clean up chat mode switching (#248270) Fix microsoft/vscode-copilot#15835 --- .../chat/browser/actions/chatActions.ts | 84 +++++++++++++++++-- .../chat/browser/actions/chatClearActions.ts | 6 +- .../browser/actions/chatExecuteActions.ts | 37 ++------ .../utils/attachInstructions.ts | 10 +-- .../contrib/chat/browser/chat.contribution.ts | 4 +- 5 files changed, 92 insertions(+), 49 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 76252e19d83..38205701388 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -24,6 +24,7 @@ import { IActionViewItemService } from '../../../../../platform/actions/browser/ import { DropdownWithPrimaryActionViewItem } from '../../../../../platform/actions/browser/dropdownWithPrimaryActionViewItem.js'; import { Action2, ICommandPaletteOptions, MenuId, MenuItemAction, MenuRegistry, registerAction2, SubmenuItemAction } from '../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IsLinuxContext, IsWindowsContext } from '../../../../../platform/contextkey/common/contextkeys.js'; import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; @@ -63,6 +64,8 @@ import { clearChatEditor } from './chatClear.js'; export const CHAT_CATEGORY = localize2('chat.category', 'Chat'); +export const ACTION_ID_NEW_CHAT = `workbench.action.chat.newChat`; +export const ACTION_ID_NEW_EDIT_SESSION = `workbench.action.chat.newEditSession`; export const CHAT_OPEN_ACTION_ID = 'workbench.action.chat.open'; export const CHAT_SETUP_ACTION_ID = 'workbench.action.chat.triggerSetup'; const TOGGLE_CHAT_ACTION_ID = 'workbench.action.chat.toggle'; @@ -120,7 +123,8 @@ abstract class OpenChatGlobalAction extends Action2 { const toolsService = accessor.get(ILanguageModelToolsService); const viewsService = accessor.get(IViewsService); const hostService = accessor.get(IHostService); - + const instaService = accessor.get(IInstantiationService); + const commandService = accessor.get(ICommandService); let chatWidget = widgetService.lastFocusedWidget; // When this was invoked to switch to a mode via keybinding, and some chat widget is focused, use that one. @@ -133,10 +137,14 @@ abstract class OpenChatGlobalAction extends Action2 { return; } - const mode = opts?.mode ?? this.mode; - if (mode && validateChatMode(mode)) { - chatWidget.input.setChatMode(mode); + let switchToMode = opts?.mode ?? this.mode; + if (!switchToMode) { + switchToMode = opts?.query.startsWith('@') ? ChatMode.Ask : undefined; } + if (switchToMode && validateChatMode(switchToMode)) { + await this.handleSwitchToMode(switchToMode, chatWidget, instaService, commandService); + } + if (opts?.previousRequests?.length && chatWidget.viewModel) { for (const { request, response } of opts.previousRequests) { chatService.addCompleteRequest(chatWidget.viewModel.sessionId, request, undefined, 0, { message: response }); @@ -149,9 +157,6 @@ abstract class OpenChatGlobalAction extends Action2 { } } if (opts?.query) { - if (opts.query.startsWith('@') && (chatWidget.input.currentMode === ChatMode.Agent || chatService.edits2Enabled)) { - chatWidget.input.setChatMode(ChatMode.Ask); - } if (opts.isPartialQuery) { chatWidget.setInput(opts.query); } else { @@ -177,6 +182,24 @@ abstract class OpenChatGlobalAction extends Action2 { chatWidget.focusInput(); } + + private async handleSwitchToMode(switchToMode: ChatMode, chatWidget: IChatWidget, instaService: IInstantiationService, commandService: ICommandService): Promise { + const currentMode = chatWidget.input.currentMode; + + if (switchToMode) { + const editingSession = chatWidget.viewModel?.model.editingSession; + const requestCount = chatWidget.viewModel?.model.getRequests().length ?? 0; + const chatModeCheck = await instaService.invokeFunction(handleModeSwitch, currentMode, switchToMode, requestCount, editingSession); + if (!chatModeCheck) { + return; + } + chatWidget.input.setChatMode(switchToMode); + + if (chatModeCheck.needToClearSession) { + await commandService.executeCommand(ACTION_ID_NEW_CHAT); + } + } + } } class PrimaryOpenChatGlobalAction extends OpenChatGlobalAction { @@ -875,6 +898,53 @@ export async function handleCurrentEditingSession(currentEditingSession: IChatEd return true; } +/** + * Returns whether we can switch the chat mode, based on whether the user had to agree to clear the session, false to cancel. + */ +export async function handleModeSwitch( + accessor: ServicesAccessor, + fromMode: ChatMode, + toMode: ChatMode, + requestCount: number, + editingSession: IChatEditingSession | undefined, +): Promise { + if (!editingSession || fromMode === toMode) { + return { needToClearSession: false }; + } + + const configurationService = accessor.get(IConfigurationService); + const dialogService = accessor.get(IDialogService); + const needToClearEdits = (!configurationService.getValue(ChatConfiguration.Edits2Enabled) && (fromMode === ChatMode.Edit || toMode === ChatMode.Edit)) && requestCount > 0; + if (needToClearEdits) { + // If not using edits2 and switching into or out of edit mode, ask to discard the session + const phrase = localize('switchMode.confirmPhrase', "Switching chat modes will end your current edit session."); + + const currentEdits = editingSession.entries.get(); + const undecidedEdits = currentEdits.filter((edit) => edit.state.get() === ModifiedFileEntryState.Modified); + if (undecidedEdits.length > 0) { + if (!await handleCurrentEditingSession(editingSession, phrase, dialogService)) { + return false; + } + + return { needToClearSession: true }; + } else { + const confirmation = await dialogService.confirm({ + title: localize('agent.newSession', "Start new session?"), + message: localize('agent.newSessionMessage', "Changing the chat mode will end your current edit session. Would you like to change the chat mode?"), + primaryButton: localize('agent.newSession.confirm', "Yes"), + type: 'info' + }); + if (!confirmation.confirmed) { + return false; + } + + return { needToClearSession: true }; + } + } + + return { needToClearSession: false }; +} + export interface IClearEditingSessionConfirmationOptions { titleOverride?: string; messageOverride?: string; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index 562a79ee3e5..85ec8d49d88 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -20,13 +20,9 @@ import { ChatMode } from '../../common/constants.js'; import { ChatViewId, IChatWidget } from '../chat.js'; import { EditingSessionAction } from '../chatEditing/chatEditingActions.js'; import { ChatEditorInput } from '../chatEditorInput.js'; -import { CHAT_CATEGORY, handleCurrentEditingSession } from './chatActions.js'; +import { ACTION_ID_NEW_CHAT, ACTION_ID_NEW_EDIT_SESSION, CHAT_CATEGORY, handleCurrentEditingSession } from './chatActions.js'; import { clearChatEditor } from './chatClear.js'; -export const ACTION_ID_NEW_CHAT = `workbench.action.chat.newChat`; -export const ACTION_ID_NEW_EDIT_SESSION = `workbench.action.chat.newEditSession`; -export const ChatDoneActionId = 'workbench.action.chat.done'; - export interface INewEditSessionActionContext { /** * An initial prompt to write to the chat. diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index ddd608dd436..4ab605c6a73 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -14,10 +14,10 @@ import { ICommandService } from '../../../../../platform/commands/common/command import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; -import { ModifiedFileEntryState } from '../../common/chatEditingService.js'; import { chatVariableLeader } from '../../common/chatParserTypes.js'; import { IChatService } from '../../common/chatService.js'; import { ChatAgentLocation, ChatConfiguration, ChatMode, validateChatMode } from '../../common/constants.js'; @@ -25,8 +25,7 @@ import { ILanguageModelChatMetadata } from '../../common/languageModels.js'; import { ILanguageModelToolsService } from '../../common/languageModelToolsService.js'; import { IChatWidget, IChatWidgetService, showChatView } from '../chat.js'; import { getEditingSessionContext } from '../chatEditing/chatEditingActions.js'; -import { CHAT_CATEGORY, handleCurrentEditingSession } from './chatActions.js'; -import { ACTION_ID_NEW_CHAT } from './chatClearActions.js'; +import { ACTION_ID_NEW_CHAT, CHAT_CATEGORY, handleCurrentEditingSession, handleModeSwitch } from './chatActions.js'; export interface IVoiceChatExecuteActionContext { readonly disableTimeout?: boolean; @@ -134,7 +133,7 @@ class ToggleChatModeAction extends Action2 { async run(accessor: ServicesAccessor, ...args: any[]) { const commandService = accessor.get(ICommandService); const configurationService = accessor.get(IConfigurationService); - const dialogService = accessor.get(IDialogService); + const instaService = accessor.get(IInstantiationService); const context = getEditingSessionContext(accessor, args); if (!context?.chatWidget) { @@ -145,41 +144,19 @@ class ToggleChatModeAction extends Action2 { const chatSession = context.chatWidget.viewModel?.model; const requestCount = chatSession?.getRequests().length ?? 0; const switchToMode = validateChatMode(arg?.mode) ?? this.getNextMode(context.chatWidget, requestCount, configurationService); - const needToClearEdits = (!configurationService.getValue(ChatConfiguration.Edits2Enabled) && (context.chatWidget.input.currentMode === ChatMode.Edit || switchToMode === ChatMode.Edit)) && requestCount > 0; if (switchToMode === context.chatWidget.input.currentMode) { return; } - if (needToClearEdits) { - // If not using edits2 and switching into or out of edit mode, ask to discard the session - const phrase = localize('switchMode.confirmPhrase', "Switching chat modes will end your current edit session."); - if (!context.editingSession) { - return; - } - - const currentEdits = context.editingSession.entries.get(); - const undecidedEdits = currentEdits.filter((edit) => edit.state.get() === ModifiedFileEntryState.Modified); - if (undecidedEdits.length > 0) { - if (!await handleCurrentEditingSession(context.editingSession, phrase, dialogService)) { - return; - } - } else { - const confirmation = await dialogService.confirm({ - title: localize('agent.newSession', "Start new session?"), - message: localize('agent.newSessionMessage', "Changing the chat mode will end your current edit session. Would you like to change the chat mode?"), - primaryButton: localize('agent.newSession.confirm', "Yes"), - type: 'info' - }); - if (!confirmation.confirmed) { - return; - } - } + const chatModeCheck = await instaService.invokeFunction(handleModeSwitch, context.chatWidget.input.currentMode, switchToMode, requestCount, context.editingSession); + if (!chatModeCheck) { + return; } context.chatWidget.input.setChatMode(switchToMode); - if (needToClearEdits) { + if (chatModeCheck.needToClearSession) { await commandService.executeCommand(ACTION_ID_NEW_CHAT); } } diff --git a/src/vs/workbench/contrib/chat/browser/actions/promptActions/dialogs/askToSelectPrompt/utils/attachInstructions.ts b/src/vs/workbench/contrib/chat/browser/actions/promptActions/dialogs/askToSelectPrompt/utils/attachInstructions.ts index 14bd701edee..6f3e213326a 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/promptActions/dialogs/askToSelectPrompt/utils/attachInstructions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/promptActions/dialogs/askToSelectPrompt/utils/attachInstructions.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChatWidget, showChatView } from '../../../../../chat.js'; -import { URI } from '../../../../../../../../../base/common/uri.js'; -import { ACTION_ID_NEW_CHAT } from '../../../../chatClearActions.js'; import { assertDefined } from '../../../../../../../../../base/common/types.js'; -import { IAttachInstructionsActionOptions } from '../../../chatAttachInstructionsAction.js'; -import { IViewsService } from '../../../../../../../../services/views/common/viewsService.js'; +import { URI } from '../../../../../../../../../base/common/uri.js'; import { ICommandService } from '../../../../../../../../../platform/commands/common/commands.js'; +import { IViewsService } from '../../../../../../../../services/views/common/viewsService.js'; +import { IChatWidget, showChatView } from '../../../../../chat.js'; +import { ACTION_ID_NEW_CHAT } from '../../../../chatActions.js'; +import { IAttachInstructionsActionOptions } from '../../../chatAttachInstructionsAction.js'; /** * Options for the {@link attachInstructionsFiles} function. diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 3ccbe624aa7..44350505827 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -54,8 +54,8 @@ import { LanguageModelToolsExtensionPointHandler } from '../common/tools/languag import { BuiltinToolsContribution } from '../common/tools/tools.js'; import { IVoiceChatService, VoiceChatService } from '../common/voiceChatService.js'; import { AgentChatAccessibilityHelp, EditsChatAccessibilityHelp, PanelChatAccessibilityHelp, QuickChatAccessibilityHelp } from './actions/chatAccessibilityHelp.js'; -import { CopilotTitleBarMenuRendering, registerChatActions } from './actions/chatActions.js'; -import { ACTION_ID_NEW_CHAT, registerNewChatActions } from './actions/chatClearActions.js'; +import { CopilotTitleBarMenuRendering, registerChatActions, ACTION_ID_NEW_CHAT } from './actions/chatActions.js'; +import { registerNewChatActions } from './actions/chatClearActions.js'; import { CodeBlockActionRendering, registerChatCodeBlockActions, registerChatCodeCompareBlockActions } from './actions/chatCodeblockActions.js'; import { registerChatContextActions } from './actions/chatContextActions.js'; import { registerChatCopyActions } from './actions/chatCopyActions.js'; From f2d908ace5050c986410de8248eef074a9517bf5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 May 2025 05:31:54 +0200 Subject: [PATCH 58/90] chat - setup dialog cleanup (#248234) --- src/vs/base/common/product.ts | 2 -- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 12 ++---------- .../contrib/chat/browser/media/chatSetup.css | 6 ------ 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index ff80ac8bd2d..2c50e7512b5 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -327,8 +327,6 @@ export interface IDefaultChatAgent { readonly chatExtensionId: string; readonly documentationUrl: string; - readonly termsStatementUrl: string; - readonly privacyStatementUrl: string; readonly skusDocumentationUrl: string; readonly publicCodeMatchesUrl: string; readonly manageSettingsUrl: string; diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index cab0f7a028b..de32b19d4a4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -72,8 +72,6 @@ const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', chatExtensionId: product.defaultChatAgent?.chatExtensionId ?? '', documentationUrl: product.defaultChatAgent?.documentationUrl ?? '', - termsStatementUrl: product.defaultChatAgent?.termsStatementUrl ?? '', - privacyStatementUrl: product.defaultChatAgent?.privacyStatementUrl ?? '', skusDocumentationUrl: product.defaultChatAgent?.skusDocumentationUrl ?? '', publicCodeMatchesUrl: product.defaultChatAgent?.publicCodeMatchesUrl ?? '', manageOveragesUrl: product.defaultChatAgent?.manageOverageUrl ?? '', @@ -90,8 +88,6 @@ const defaultChat = { chatRefreshTokenCommand: product.defaultChatAgent?.chatRefreshTokenCommand ?? '', }; -const copilotSettingsMessage = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot Free and Pro may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); - //#region Contribution const ToolsAgentContextKey = ContextKeyExpr.and( @@ -410,7 +406,7 @@ class SetupAgent extends Disposable implements IChatAgentImplementation { if (result.dialogSkipped) { progress({ kind: 'markdownContent', - content: new MarkdownString([localize('copilotSetupSuccess', "Copilot setup finished successfully."), copilotSettingsMessage].join('\n\n')) + content: new MarkdownString(localize('copilotSetupSuccess', "Copilot setup finished successfully.")) }); } else if (requestModel) { let newRequest = this.replaceAgentInRequestModel(requestModel, chatAgentService); // Replace agent part with the actual Copilot agent... @@ -724,13 +720,9 @@ class ChatSetup { const header = localize({ key: 'headerDialog', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer. Write code faster with completions, fix bugs and build new features across multiple files, and learn about your codebase through chat.", defaultChat.documentationUrl); element.appendChild($('p.setup-header', undefined, disposables.add(markdown.render(new MarkdownString(header, { isTrusted: true }))).element)); - // Terms - const terms = localize({ key: 'terms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); - element.appendChild($('p.setup-legal', undefined, disposables.add(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element)); - // SKU Settings if (this.telemetryService.telemetryLevel !== TelemetryLevel.NONE) { - const settings = copilotSettingsMessage; + const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "GitHub Copilot Free, Pro and Pro+ may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); element.appendChild($('p.setup-settings', undefined, disposables.add(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element)); } diff --git a/src/vs/workbench/contrib/chat/browser/media/chatSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatSetup.css index 3d1e37d5e2f..57238c0c4a9 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatSetup.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatSetup.css @@ -11,12 +11,6 @@ width: 100%; } - p.setup-legal { - font-size: 12px; - color: var(--vscode-descriptionForeground); - margin-top: 2em; - } - p.setup-settings { font-size: 12px; color: var(--vscode-descriptionForeground); From c398c11d808dc484db9358cd054da36dd6e8640c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 May 2025 06:25:07 +0200 Subject: [PATCH 59/90] welcome - align copilot settings text (#248277) --- .../welcomeGettingStarted/common/gettingStartedContent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 72b6da5f26a..044c5c46a5c 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -19,7 +19,7 @@ interface IGettingStartedContentProvider { (): string; } -export const copilotSettingsMessage = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot Free and Pro may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", product.defaultChatAgent?.publicCodeMatchesUrl, product.defaultChatAgent?.manageSettingsUrl); +export const copilotSettingsMessage = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "GitHub Copilot Free, Pro and Pro+ may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", product.defaultChatAgent?.publicCodeMatchesUrl, product.defaultChatAgent?.manageSettingsUrl); class GettingStartedContentProviderRegistry { From 83d1ff749141e435b3b135079a02f8f515621eec Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 May 2025 06:35:39 +0200 Subject: [PATCH 60/90] process explorer - turn on `useNewProcessExplorer` (#248276) --- src/vs/workbench/electron-sandbox/desktop.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 1b4d7c1dd9d..ad4a5fc72d3 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -30,7 +30,6 @@ import { applicationConfigurationNodeBase, securityConfigurationNodeBase } from import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from '../../platform/window/electron-sandbox/window.js'; import { DefaultAccountManagementContribution } from '../services/accounts/common/defaultAccount.js'; import { registerWorkbenchContribution2, WorkbenchPhase } from '../common/contributions.js'; -import product from '../../platform/product/common/product.js'; // Actions (function registerActions(): void { @@ -151,7 +150,7 @@ import product from '../../platform/product/common/product.js'; }, 'application.useNewProcessExplorer': { 'type': 'boolean', - 'default': product.quality !== 'stable', // TODO@bpasero decide on a default + 'default': true, // TODO@bpasero remove me when done 'description': localize('useNewProcessExplorer', "Controls whether a the process explorer opens in a floating window."), }, } From c28d69898a907fbd03b28b43dd8359c96c2e4d49 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 May 2025 08:34:42 +0200 Subject: [PATCH 61/90] make sure to await accepting automatically so that transaction doesn't trip (#248280) re https://github.com/microsoft/vscode-copilot-release/issues/9087 --- .../chat/browser/chatEditing/chatEditingModifiedFileEntry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index e0b5ebe19ed..b22f29e571f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts @@ -285,7 +285,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im if (await this._areOriginalAndModifiedIdentical()) { // ACCEPT if identical - this.accept(tx); + await this.accept(tx); } } From 58592ced33d53264083fa77e1f99581bbaffbcd9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 May 2025 08:58:28 +0200 Subject: [PATCH 62/90] fix: correct import path for ProcessExplorerEditorInput and add new editor input class (#248283) --- .../contrib/issue/electron-sandbox/process.contribution.ts | 2 +- .../electron-sandbox/processExplorer.contribution.ts | 2 +- ...ocessExplorerEditoInput.ts => processExplorerEditorInput.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/vs/workbench/contrib/processExplorer/electron-sandbox/{processExplorerEditoInput.ts => processExplorerEditorInput.ts} (100%) diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts b/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts index f336def40a3..116d8d57839 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts @@ -18,7 +18,7 @@ import './processService.js'; import './processMainService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { AUX_WINDOW_GROUP, IEditorService } from '../../../services/editor/common/editorService.js'; -import { ProcessExplorerEditorInput } from '../../processExplorer/electron-sandbox/processExplorerEditoInput.js'; +import { ProcessExplorerEditorInput } from '../../processExplorer/electron-sandbox/processExplorerEditorInput.js'; //#region Commands diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts index f1d943c1be6..7424d9b0412 100644 --- a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorer.contribution.ts @@ -12,7 +12,7 @@ import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase import { IEditorSerializer, EditorExtensions, IEditorFactoryRegistry } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { IEditorResolverService, RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js'; -import { ProcessExplorerEditorInput } from './processExplorerEditoInput.js'; +import { ProcessExplorerEditorInput } from './processExplorerEditorInput.js'; import { ProcessExplorerEditor } from './processExplorerEditor.js'; class ProcessExplorerEditorContribution implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditoInput.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditorInput.ts similarity index 100% rename from src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditoInput.ts rename to src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerEditorInput.ts From 94590884c8b9516a37211b811e63992740bc76c1 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Wed, 7 May 2025 01:24:21 -0700 Subject: [PATCH 63/90] refactor ui element selection to standalone service (#248185) * big refator to ui element's own service * move native to own file * add native type * fix in layer checker * fix in layer checker * fix whitespace * fix another whitespace * Update layersChecker.js * build still not passing * Update layersChecker.js * update layerChecker using npm run compile * try updating layer checker rules * rename simple browser service * another rename in app.ts --- build/lib/layersChecker.js | 23 +- build/lib/layersChecker.ts | 25 +- src/vs/code/electron-main/app.ts | 9 + .../browserElements/common/browserElements.ts | 26 ++ .../common/nativeBrowserElementsService.ts | 30 ++ .../nativeBrowserElementsMainService.ts | 370 ++++++++++++++++++ src/vs/platform/native/common/native.ts | 8 - .../electron-main/nativeHostMainService.ts | 315 +-------------- .../chatEditing/simpleBrowserEditorOverlay.ts | 5 +- .../browser/browserElementsService.ts | 18 + .../browserElementsService.ts | 54 +++ .../host/browser/browserHostService.ts | 5 - .../workbench/services/host/browser/host.ts | 4 - .../electron-sandbox/nativeHostService.ts | 23 +- .../test/browser/workbenchTestServices.ts | 2 - .../electron-sandbox/workbenchTestServices.ts | 3 +- src/vs/workbench/workbench.desktop.main.ts | 1 + .../workbench/workbench.web.main.internal.ts | 1 + 18 files changed, 562 insertions(+), 360 deletions(-) create mode 100644 src/vs/platform/browserElements/common/browserElements.ts create mode 100644 src/vs/platform/browserElements/common/nativeBrowserElementsService.ts create mode 100644 src/vs/platform/browserElements/electron-main/nativeBrowserElementsMainService.ts create mode 100644 src/vs/workbench/services/browserElements/browser/browserElementsService.ts create mode 100644 src/vs/workbench/services/browserElements/electron-sandbox/browserElementsService.ts diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index 83bd45e9d3f..ab49ef0537b 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -103,7 +103,8 @@ const NATIVE_TYPES = [ 'INativeWindowConfiguration', 'ICommonNativeHostService', 'INativeHostService', - 'IMainProcessService' + 'IMainProcessService', + 'INativeBrowserElementsService', ]; const RULES = [ // Tests: skip @@ -176,6 +177,26 @@ const RULES = [ '@types/node' // no node.js ] }, + // Common: vs/platform/browserElements/common/browserElements.ts + { + target: '**/vs/platform/browserElements/common/browserElements.ts', + allowedTypes: CORE_TYPES, + disallowedTypes: [ /* Ignore native types that are defined from here */], + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + // Common: vs/platform/browserElements/common/nativeBrowserElementsService.ts + { + target: '**/vs/platform/browserElements/common/nativeBrowserElementsService.ts', + allowedTypes: CORE_TYPES, + disallowedTypes: [ /* Ignore native types that are defined from here */], + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, // Common: vs/platform/native/common/native.ts { target: '**/vs/platform/native/common/native.ts', diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index 677b505ab22..6c41621a1d3 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -104,7 +104,8 @@ const NATIVE_TYPES = [ 'INativeWindowConfiguration', 'ICommonNativeHostService', 'INativeHostService', - 'IMainProcessService' + 'IMainProcessService', + 'INativeBrowserElementsService', ]; const RULES: IRule[] = [ @@ -188,6 +189,28 @@ const RULES: IRule[] = [ ] }, + // Common: vs/platform/browserElements/common/browserElements.ts + { + target: '**/vs/platform/browserElements/common/browserElements.ts', + allowedTypes: CORE_TYPES, + disallowedTypes: [/* Ignore native types that are defined from here */], + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + + // Common: vs/platform/browserElements/common/nativeBrowserElementsService.ts + { + target: '**/vs/platform/browserElements/common/nativeBrowserElementsService.ts', + allowedTypes: CORE_TYPES, + disallowedTypes: [/* Ignore native types that are defined from here */], + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + // Common: vs/platform/native/common/native.ts { target: '**/vs/platform/native/common/native.ts', diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 588e1cb4521..2c6cc9b6eec 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -35,6 +35,7 @@ import { DiagnosticsMainService, IDiagnosticsMainService } from '../../platform/ import { DialogMainService, IDialogMainService } from '../../platform/dialogs/electron-main/dialogMainService.js'; import { IEncryptionMainService } from '../../platform/encryption/common/encryptionService.js'; import { EncryptionMainService } from '../../platform/encryption/electron-main/encryptionMainService.js'; +import { NativeBrowserElementsMainService, INativeBrowserElementsMainService } from '../../platform/browserElements/electron-main/nativeBrowserElementsMainService.js'; import { NativeParsedArgs } from '../../platform/environment/common/argv.js'; import { IEnvironmentMainService } from '../../platform/environment/electron-main/environmentMainService.js'; import { isLaunchedFromCli } from '../../platform/environment/node/argvHelper.js'; @@ -1023,6 +1024,9 @@ export class CodeApplication extends Disposable { // Encryption services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService)); + // Browser Elements + services.set(INativeBrowserElementsMainService, new SyncDescriptor(NativeBrowserElementsMainService, undefined, false /* proxied to other processes */)); + // Keyboard Layout services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService)); @@ -1165,6 +1169,11 @@ export class CodeApplication extends Disposable { const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService), disposables); mainProcessElectronServer.registerChannel('encryption', encryptionChannel); + // Browser Elements + const browserElementsChannel = ProxyChannel.fromService(accessor.get(INativeBrowserElementsMainService), disposables); + mainProcessElectronServer.registerChannel('browserElements', browserElementsChannel); + sharedProcessClient.then(client => client.registerChannel('browserElements', browserElementsChannel)); + // Signing const signChannel = ProxyChannel.fromService(accessor.get(ISignService), disposables); mainProcessElectronServer.registerChannel('sign', signChannel); diff --git a/src/vs/platform/browserElements/common/browserElements.ts b/src/vs/platform/browserElements/common/browserElements.ts new file mode 100644 index 00000000000..08c30519ba6 --- /dev/null +++ b/src/vs/platform/browserElements/common/browserElements.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from '../../../base/common/cancellation.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { IRectangle } from '../../window/common/window.js'; + +export const INativeBrowserElementsService = createDecorator('nativeBrowserElementsService'); + +export interface IElementData { + readonly outerHTML: string; + readonly computedStyle: string; + readonly bounds: IRectangle; +} + +export interface INativeBrowserElementsService { + + readonly _serviceBrand: undefined; + + // Properties + readonly windowId: number; + + getElementData(rect: IRectangle, token: CancellationToken, cancellationId?: number): Promise; +} diff --git a/src/vs/platform/browserElements/common/nativeBrowserElementsService.ts b/src/vs/platform/browserElements/common/nativeBrowserElementsService.ts new file mode 100644 index 00000000000..4bc48e24f04 --- /dev/null +++ b/src/vs/platform/browserElements/common/nativeBrowserElementsService.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { IMainProcessService } from '../../ipc/common/mainProcessService.js'; +import { INativeBrowserElementsService } from './browserElements.js'; + +// @ts-ignore: interface is implemented via proxy +export class NativeBrowserElementsService implements INativeBrowserElementsService { + + declare readonly _serviceBrand: undefined; + + constructor( + readonly windowId: number, + @IMainProcessService mainProcessService: IMainProcessService + ) { + return ProxyChannel.toService(mainProcessService.getChannel('browserElements'), { + context: windowId, + properties: (() => { + const properties = new Map(); + properties.set('windowId', windowId); + + return properties; + })() + }); + } +} + diff --git a/src/vs/platform/browserElements/electron-main/nativeBrowserElementsMainService.ts b/src/vs/platform/browserElements/electron-main/nativeBrowserElementsMainService.ts new file mode 100644 index 00000000000..757db569244 --- /dev/null +++ b/src/vs/platform/browserElements/electron-main/nativeBrowserElementsMainService.ts @@ -0,0 +1,370 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IElementData, INativeBrowserElementsService } from '../common/browserElements.js'; +import { CancellationToken } from '../../../base/common/cancellation.js'; +import { IRectangle } from '../../window/common/window.js'; +import { BrowserWindow, webContents } from 'electron'; +import { IAuxiliaryWindow } from '../../auxiliaryWindow/electron-main/auxiliaryWindow.js'; +import { ICodeWindow } from '../../window/electron-main/window.js'; +import { IAuxiliaryWindowsMainService } from '../../auxiliaryWindow/electron-main/auxiliaryWindows.js'; +import { IWindowsMainService } from '../../windows/electron-main/windows.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { Disposable } from '../../../base/common/lifecycle.js'; +import { AddFirstParameterToFunctions } from '../../../base/common/types.js'; + + +export const INativeBrowserElementsMainService = createDecorator('browserElementsMainService'); +export interface INativeBrowserElementsMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } + +interface NodeDataResponse { + outerHTML: string; + computedStyle: string; + bounds: IRectangle; +} + +export class NativeBrowserElementsMainService extends Disposable implements INativeBrowserElementsMainService { + _serviceBrand: undefined; + + constructor( + @IWindowsMainService private readonly windowsMainService: IWindowsMainService, + @IAuxiliaryWindowsMainService private readonly auxiliaryWindowsMainService: IAuxiliaryWindowsMainService, + + ) { + super(); + } + + get windowId(): never { throw new Error('Not implemented in electron-main'); } + + async getElementData(windowId: number | undefined, rect: IRectangle, token: CancellationToken, cancellationId?: number): Promise { + const window = this.windowById(windowId); + if (!window?.win) { + return undefined; + } + + // Find the simple browser webview + const allWebContents = webContents.getAllWebContents(); + const simpleBrowserWebview = allWebContents.find(webContent => webContent.id === window.id); + + if (!simpleBrowserWebview) { + return undefined; + } + + const debuggers = simpleBrowserWebview.debugger; + debuggers.attach(); + + const { targetInfos } = await debuggers.sendCommand('Target.getTargets'); + let resultId: string | undefined = undefined; + let target: typeof targetInfos[number] | undefined = undefined; + let targetSessionId: number | undefined = undefined; + try { + // find parent id and extract id + const matchingTarget = targetInfos.find((targetInfo: { url: string }) => { + const url = new URL(targetInfo.url); + return url.searchParams.get('parentId') === window?.id.toString() && url.searchParams.get('extensionId') === 'vscode.simple-browser'; + }); + + if (matchingTarget) { + const url = new URL(matchingTarget.url); + resultId = url.searchParams.get('id')!; + } + + // use id to grab simple browser target + if (resultId) { + target = targetInfos.find((targetInfo: { url: string }) => { + const url = new URL(targetInfo.url); + return url.searchParams.get('id') === resultId && url.searchParams.get('vscodeBrowserReqId')!; + }); + } + + const { sessionId } = await debuggers.sendCommand('Target.attachToTarget', { + targetId: target.targetId, + flatten: true, + }); + + targetSessionId = sessionId; + + await debuggers.sendCommand('DOM.enable', {}, sessionId); + await debuggers.sendCommand('CSS.enable', {}, sessionId); + await debuggers.sendCommand('Overlay.enable', {}, sessionId); + await debuggers.sendCommand('Debugger.enable', {}, sessionId); + await debuggers.sendCommand('Runtime.enable', {}, sessionId); + + await debuggers.sendCommand('Runtime.evaluate', { + expression: `(function() { + const style = document.createElement('style'); + style.id = '__pseudoBlocker__'; + style.textContent = '*::before, *::after { pointer-events: none !important; }'; + document.head.appendChild(style); + })();`, + }, sessionId); + + // slightly changed default CDP debugger inspect colors + await debuggers.sendCommand('Overlay.setInspectMode', { + mode: 'searchForNode', + highlightConfig: { + showInfo: true, + showRulers: false, + showStyles: true, + showAccessibilityInfo: true, + showExtensionLines: false, + contrastAlgorithm: 'aa', + contentColor: { r: 173, g: 216, b: 255, a: 0.8 }, + paddingColor: { r: 150, g: 200, b: 255, a: 0.5 }, + borderColor: { r: 120, g: 180, b: 255, a: 0.7 }, + marginColor: { r: 200, g: 220, b: 255, a: 0.4 }, + eventTargetColor: { r: 130, g: 160, b: 255, a: 0.8 }, + shapeColor: { r: 130, g: 160, b: 255, a: 0.8 }, + shapeMarginColor: { r: 130, g: 160, b: 255, a: 0.5 }, + gridHighlightConfig: { + rowGapColor: { r: 140, g: 190, b: 255, a: 0.3 }, + rowHatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, + columnGapColor: { r: 140, g: 190, b: 255, a: 0.3 }, + columnHatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, + rowLineColor: { r: 120, g: 180, b: 255 }, + columnLineColor: { r: 120, g: 180, b: 255 }, + rowLineDash: true, + columnLineDash: true + }, + flexContainerHighlightConfig: { + containerBorder: { + color: { r: 120, g: 180, b: 255 }, + pattern: 'solid' + }, + itemSeparator: { + color: { r: 140, g: 190, b: 255 }, + pattern: 'solid' + }, + lineSeparator: { + color: { r: 140, g: 190, b: 255 }, + pattern: 'solid' + }, + mainDistributedSpace: { + hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, + fillColor: { r: 140, g: 190, b: 255, a: 0.4 } + }, + crossDistributedSpace: { + hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, + fillColor: { r: 140, g: 190, b: 255, a: 0.4 } + }, + rowGapSpace: { + hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, + fillColor: { r: 140, g: 190, b: 255, a: 0.4 } + }, + columnGapSpace: { + hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, + fillColor: { r: 140, g: 190, b: 255, a: 0.4 } + } + }, + flexItemHighlightConfig: { + baseSizeBox: { + hatchColor: { r: 130, g: 170, b: 255, a: 0.6 } + }, + baseSizeBorder: { + color: { r: 120, g: 180, b: 255 }, + pattern: 'solid' + }, + flexibilityArrow: { + color: { r: 130, g: 190, b: 255 } + } + }, + }, + }, sessionId); + } catch (e) { + debuggers.detach(); + throw new Error('No target found', e); + } + + if (!targetSessionId) { + debuggers.detach(); + throw new Error('No target session id found'); + } + + const nodeData = await this.getNodeData(targetSessionId, debuggers, window.win, cancellationId); + debuggers.detach(); + + const zoomFactor = simpleBrowserWebview.getZoomFactor(); + const absoluteBounds = { + x: rect.x + nodeData.bounds.x, + y: rect.y + nodeData.bounds.y, + width: nodeData.bounds.width, + height: nodeData.bounds.height + }; + + const clippedBounds = { + x: Math.max(absoluteBounds.x, rect.x), + y: Math.max(absoluteBounds.y, rect.y), + width: Math.max(0, Math.min(absoluteBounds.x + absoluteBounds.width, rect.x + rect.width) - Math.max(absoluteBounds.x, rect.x)), + height: Math.max(0, Math.min(absoluteBounds.y + absoluteBounds.height, rect.y + rect.height) - Math.max(absoluteBounds.y, rect.y)) + }; + + const scaledBounds = { + x: clippedBounds.x * zoomFactor, + y: clippedBounds.y * zoomFactor, + width: clippedBounds.width * zoomFactor, + height: clippedBounds.height * zoomFactor + }; + + return { outerHTML: nodeData.outerHTML, computedStyle: nodeData.computedStyle, bounds: scaledBounds }; + } + + async getNodeData(sessionId: number, debuggers: any, window: BrowserWindow, cancellationId?: number): Promise { + return new Promise((resolve, reject) => { + const onMessage = async (event: any, method: string, params: { backendNodeId: number }) => { + if (method === 'Overlay.inspectNodeRequested') { + debuggers.off('message', onMessage); + await debuggers.sendCommand('Runtime.evaluate', { + expression: `(() => { + const style = document.getElementById('__pseudoBlocker__'); + if (style) style.remove(); + })();`, + }, sessionId); + + const backendNodeId = params?.backendNodeId; + if (!backendNodeId) { + throw new Error('Missing backendNodeId in inspectNodeRequested event'); + } + + try { + await debuggers.sendCommand('DOM.getDocument', {}, sessionId); + const { nodeIds } = await debuggers.sendCommand('DOM.pushNodesByBackendIdsToFrontend', { backendNodeIds: [backendNodeId] }, sessionId); + if (!nodeIds || nodeIds.length === 0) { + throw new Error('Failed to get node IDs.'); + } + const nodeId = nodeIds[0]; + + const { model } = await debuggers.sendCommand('DOM.getBoxModel', { nodeId }, sessionId); + if (!model) { + throw new Error('Failed to get box model.'); + } + + const content = model.content; + const margin = model.margin; + const x = Math.min(margin[0], content[0]); + const y = Math.min(margin[1], content[1]) + 32.4; // 32.4 is height of the title bar + const width = Math.max(margin[2] - margin[0], content[2] - content[0]); + const height = Math.max(margin[5] - margin[1], content[5] - content[1]); + + const matched = await debuggers.sendCommand('CSS.getMatchedStylesForNode', { nodeId }, sessionId); + if (!matched) { + throw new Error('Failed to get matched css.'); + } + + const formatted = this.formatMatchedStyles(matched); + const { outerHTML } = await debuggers.sendCommand('DOM.getOuterHTML', { nodeId }, sessionId); + if (!outerHTML) { + throw new Error('Failed to get outerHTML.'); + } + + resolve({ + outerHTML, + computedStyle: formatted, + bounds: { x, y, width, height } + }); + } catch (err) { + debuggers.off('message', onMessage); + debuggers.detach(); + reject(err); + } + } + }; + + window.webContents.on('ipc-message', async (event, channel, closedCancellationId) => { + if (channel === `vscode:cancelElementSelection${cancellationId}`) { + if (cancellationId !== closedCancellationId) { + return; + } + debuggers.off('message', onMessage); + if (debuggers.isAttached()) { + debuggers.detach(); + } + window.webContents.removeAllListeners('ipc-message'); + } + }); + + debuggers.on('message', onMessage); + }); + } + + formatMatchedStyles(matched: any): string { + const lines: string[] = []; + + // inline + if (matched.inlineStyle?.cssProperties?.length) { + lines.push('/* Inline style */'); + lines.push('element {'); + for (const prop of matched.inlineStyle.cssProperties) { + if (prop.name && prop.value) { + lines.push(` ${prop.name}: ${prop.value};`); + } + } + lines.push('}\n'); + } + + // matched + if (matched.matchedCSSRules?.length) { + for (const ruleEntry of matched.matchedCSSRules) { + const rule = ruleEntry.rule; + const selectors = rule.selectorList.selectors.map((s: any) => s.text).join(', '); + lines.push(`/* Matched Rule from ${rule.origin} */`); + lines.push(`${selectors} {`); + for (const prop of rule.style.cssProperties) { + if (prop.name && prop.value) { + lines.push(` ${prop.name}: ${prop.value};`); + } + } + lines.push('}\n'); + } + } + + // inherited rules + if (matched.inherited?.length) { + let level = 1; + for (const inherited of matched.inherited) { + const rules = inherited.matchedCSSRules || []; + for (const ruleEntry of rules) { + const rule = ruleEntry.rule; + const selectors = rule.selectorList.selectors.map((s: any) => s.text).join(', '); + lines.push(`/* Inherited from ancestor level ${level} (${rule.origin}) */`); + lines.push(`${selectors} {`); + for (const prop of rule.style.cssProperties) { + if (prop.name && prop.value) { + lines.push(` ${prop.name}: ${prop.value};`); + } + } + lines.push('}\n'); + } + level++; + } + } + + return '\n' + lines.join('\n'); + } + + private windowById(windowId: number | undefined, fallbackCodeWindowId?: number): ICodeWindow | IAuxiliaryWindow | undefined { + return this.codeWindowById(windowId) ?? this.auxiliaryWindowById(windowId) ?? this.codeWindowById(fallbackCodeWindowId); + } + + private codeWindowById(windowId: number | undefined): ICodeWindow | undefined { + if (typeof windowId !== 'number') { + return undefined; + } + + return this.windowsMainService.getWindowById(windowId); + } + + private auxiliaryWindowById(windowId: number | undefined): IAuxiliaryWindow | undefined { + if (typeof windowId !== 'number') { + return undefined; + } + + const contents = webContents.fromId(windowId); + if (!contents) { + return undefined; + } + + return this.auxiliaryWindowsMainService.getWindowByWebContents(contents); + } +} diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 9394408888f..a55e5b390ed 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from '../../../base/common/buffer.js'; -import { CancellationToken } from '../../../base/common/cancellation.js'; import { Event } from '../../../base/common/event.js'; import { URI } from '../../../base/common/uri.js'; import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogOptions, SaveDialogReturnValue } from '../../../base/parts/sandbox/common/electronTypes.js'; @@ -39,12 +38,6 @@ export interface INativeHostOptions { readonly targetWindowId?: number; } -export interface IElementData { - readonly outerHTML: string; - readonly computedStyle: string; - readonly bounds: IRectangle; -} - export interface ICommonNativeHostService { readonly _serviceBrand: undefined; @@ -156,7 +149,6 @@ export interface ICommonNativeHostService { // Screenshots getScreenshot(rect?: IRectangle): Promise; - getElementData(rect: IRectangle, token: CancellationToken, cancellationId?: number): Promise; // Process getProcessId(): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 45964bcdcc2..cac3b314a64 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -28,7 +28,7 @@ import { IEnvironmentMainService } from '../../environment/electron-main/environ import { createDecorator, IInstantiationService } from '../../instantiation/common/instantiation.js'; import { ILifecycleMainService, IRelaunchOptions } from '../../lifecycle/electron-main/lifecycleMainService.js'; import { ILogService } from '../../log/common/log.js'; -import { ICommonNativeHostService, IElementData, INativeHostOptions, IOSProperties, IOSStatistics } from '../common/native.js'; +import { ICommonNativeHostService, INativeHostOptions, IOSProperties, IOSStatistics } from '../common/native.js'; import { IProductService } from '../../product/common/productService.js'; import { IPartsSplash } from '../../theme/common/themeService.js'; import { IThemeMainService } from '../../theme/electron-main/themeMainService.js'; @@ -48,18 +48,11 @@ import { IConfigurationService } from '../../configuration/common/configuration. import { IProxyAuthService } from './auth.js'; import { AuthInfo, Credentials, IRequestService } from '../../request/common/request.js'; import { randomPath } from '../../../base/common/extpath.js'; -import { CancellationToken } from '../../../base/common/cancellation.js'; export interface INativeHostMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } export const INativeHostMainService = createDecorator('nativeHostMainService'); -interface NodeDataResponse { - outerHTML: string; - computedStyle: string; - bounds: IRectangle; -} - export class NativeHostMainService extends Disposable implements INativeHostMainService { declare readonly _serviceBrand: undefined; @@ -757,312 +750,6 @@ export class NativeHostMainService extends Disposable implements INativeHostMain return buf && VSBuffer.wrap(buf); } - async getElementData(windowId: number | undefined, rect: IRectangle, token: CancellationToken, cancellationId?: number): Promise { - const window = this.windowById(windowId, windowId); - if (!window?.win) { - return undefined; - } - - // Find the simple browser webview - const allWebContents = webContents.getAllWebContents(); - const simpleBrowserWebview = allWebContents.find(webContent => webContent.id === windowId); - - if (!simpleBrowserWebview) { - return undefined; - } - - const debuggers = simpleBrowserWebview.debugger; - debuggers.attach(); - - const { targetInfos } = await debuggers.sendCommand('Target.getTargets'); - let resultId: string | undefined = undefined; - let target: typeof targetInfos[number] | undefined = undefined; - let targetSessionId: number | undefined = undefined; - try { - // find parent id and extract id - const matchingTarget = targetInfos.find((targetInfo: { url: string }) => { - const url = new URL(targetInfo.url); - return url.searchParams.get('parentId') === window?.id.toString() && url.searchParams.get('extensionId') === 'vscode.simple-browser'; - }); - - if (matchingTarget) { - const url = new URL(matchingTarget.url); - resultId = url.searchParams.get('id')!; - } - - // use id to grab simple browser target - if (resultId) { - target = targetInfos.find((targetInfo: { url: string }) => { - const url = new URL(targetInfo.url); - return url.searchParams.get('id') === resultId && url.searchParams.get('vscodeBrowserReqId')!; - }); - } - - const { sessionId } = await debuggers.sendCommand('Target.attachToTarget', { - targetId: target.targetId, - flatten: true, - }); - - targetSessionId = sessionId; - - await debuggers.sendCommand('DOM.enable', {}, sessionId); - await debuggers.sendCommand('CSS.enable', {}, sessionId); - await debuggers.sendCommand('Overlay.enable', {}, sessionId); - await debuggers.sendCommand('Debugger.enable', {}, sessionId); - await debuggers.sendCommand('Runtime.enable', {}, sessionId); - - await debuggers.sendCommand('Runtime.evaluate', { - expression: `(function() { - const style = document.createElement('style'); - style.id = '__pseudoBlocker__'; - style.textContent = '*::before, *::after { pointer-events: none !important; }'; - document.head.appendChild(style); - })();`, - }, sessionId); - - // slightly changed default CDP debugger inspect colors - await debuggers.sendCommand('Overlay.setInspectMode', { - mode: 'searchForNode', - highlightConfig: { - showInfo: true, - showRulers: false, - showStyles: true, - showAccessibilityInfo: true, - showExtensionLines: false, - contrastAlgorithm: 'aa', - contentColor: { r: 173, g: 216, b: 255, a: 0.8 }, - paddingColor: { r: 150, g: 200, b: 255, a: 0.5 }, - borderColor: { r: 120, g: 180, b: 255, a: 0.7 }, - marginColor: { r: 200, g: 220, b: 255, a: 0.4 }, - eventTargetColor: { r: 130, g: 160, b: 255, a: 0.8 }, - shapeColor: { r: 130, g: 160, b: 255, a: 0.8 }, - shapeMarginColor: { r: 130, g: 160, b: 255, a: 0.5 }, - gridHighlightConfig: { - rowGapColor: { r: 140, g: 190, b: 255, a: 0.3 }, - rowHatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, - columnGapColor: { r: 140, g: 190, b: 255, a: 0.3 }, - columnHatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, - rowLineColor: { r: 120, g: 180, b: 255 }, - columnLineColor: { r: 120, g: 180, b: 255 }, - rowLineDash: true, - columnLineDash: true - }, - flexContainerHighlightConfig: { - containerBorder: { - color: { r: 120, g: 180, b: 255 }, - pattern: 'solid' - }, - itemSeparator: { - color: { r: 140, g: 190, b: 255 }, - pattern: 'solid' - }, - lineSeparator: { - color: { r: 140, g: 190, b: 255 }, - pattern: 'solid' - }, - mainDistributedSpace: { - hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, - fillColor: { r: 140, g: 190, b: 255, a: 0.4 } - }, - crossDistributedSpace: { - hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, - fillColor: { r: 140, g: 190, b: 255, a: 0.4 } - }, - rowGapSpace: { - hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, - fillColor: { r: 140, g: 190, b: 255, a: 0.4 } - }, - columnGapSpace: { - hatchColor: { r: 140, g: 190, b: 255, a: 0.7 }, - fillColor: { r: 140, g: 190, b: 255, a: 0.4 } - } - }, - flexItemHighlightConfig: { - baseSizeBox: { - hatchColor: { r: 130, g: 170, b: 255, a: 0.6 } - }, - baseSizeBorder: { - color: { r: 120, g: 180, b: 255 }, - pattern: 'solid' - }, - flexibilityArrow: { - color: { r: 130, g: 190, b: 255 } - } - }, - }, - }, sessionId); - } catch (e) { - debuggers.detach(); - throw new Error('No target found', e); - } - - if (!targetSessionId) { - debuggers.detach(); - throw new Error('No target session id found'); - } - - const nodeData = await this.getNodeData(targetSessionId, debuggers, window.win, cancellationId); - debuggers.detach(); - - const zoomFactor = simpleBrowserWebview.getZoomFactor(); - const absoluteBounds = { - x: rect.x + nodeData.bounds.x, - y: rect.y + nodeData.bounds.y, - width: nodeData.bounds.width, - height: nodeData.bounds.height - }; - - const clippedBounds = { - x: Math.max(absoluteBounds.x, rect.x), - y: Math.max(absoluteBounds.y, rect.y), - width: Math.max(0, Math.min(absoluteBounds.x + absoluteBounds.width, rect.x + rect.width) - Math.max(absoluteBounds.x, rect.x)), - height: Math.max(0, Math.min(absoluteBounds.y + absoluteBounds.height, rect.y + rect.height) - Math.max(absoluteBounds.y, rect.y)) - }; - - const scaledBounds = { - x: clippedBounds.x * zoomFactor, - y: clippedBounds.y * zoomFactor, - width: clippedBounds.width * zoomFactor, - height: clippedBounds.height * zoomFactor - }; - - return { outerHTML: nodeData.outerHTML, computedStyle: nodeData.computedStyle, bounds: scaledBounds }; - } - - async getNodeData(sessionId: number, debuggers: any, window: BrowserWindow, cancellationId?: number): Promise { - return new Promise((resolve, reject) => { - const onMessage = async (event: any, method: string, params: { backendNodeId: number }) => { - if (method === 'Overlay.inspectNodeRequested') { - debuggers.off('message', onMessage); - await debuggers.sendCommand('Runtime.evaluate', { - expression: `(() => { - const style = document.getElementById('__pseudoBlocker__'); - if (style) style.remove(); - })();`, - }, sessionId); - - const backendNodeId = params?.backendNodeId; - if (!backendNodeId) { - throw new Error('Missing backendNodeId in inspectNodeRequested event'); - } - - try { - await debuggers.sendCommand('DOM.getDocument', {}, sessionId); - const { nodeIds } = await debuggers.sendCommand('DOM.pushNodesByBackendIdsToFrontend', { backendNodeIds: [backendNodeId] }, sessionId); - if (!nodeIds || nodeIds.length === 0) { - throw new Error('Failed to get node IDs.'); - } - const nodeId = nodeIds[0]; - - const { model } = await debuggers.sendCommand('DOM.getBoxModel', { nodeId }, sessionId); - if (!model) { - throw new Error('Failed to get box model.'); - } - - const content = model.content; - const margin = model.margin; - const x = Math.min(margin[0], content[0]); - const y = Math.min(margin[1], content[1]) + 32.4; // 32.4 is height of the title bar - const width = Math.max(margin[2] - margin[0], content[2] - content[0]); - const height = Math.max(margin[5] - margin[1], content[5] - content[1]); - - const matched = await debuggers.sendCommand('CSS.getMatchedStylesForNode', { nodeId }, sessionId); - if (!matched) { - throw new Error('Failed to get matched css.'); - } - - const formatted = this.formatMatchedStyles(matched); - const { outerHTML } = await debuggers.sendCommand('DOM.getOuterHTML', { nodeId }, sessionId); - if (!outerHTML) { - throw new Error('Failed to get outerHTML.'); - } - - resolve({ - outerHTML, - computedStyle: formatted, - bounds: { x, y, width, height } - }); - } catch (err) { - debuggers.off('message', onMessage); - debuggers.detach(); - reject(err); - - } - } - }; - - window.webContents.on('ipc-message', async (event, channel, closedCancellationId) => { - if (channel === `vscode:cancelElementSelection${cancellationId}`) { - if (cancellationId !== closedCancellationId) { - return; - } - debuggers.off('message', onMessage); - if (debuggers.isAttached()) { - debuggers.detach(); - } - window.webContents.removeAllListeners('ipc-message'); - } - }); - - debuggers.on('message', onMessage); - }); - } - - formatMatchedStyles(matched: any): string { - const lines: string[] = []; - - // inline - if (matched.inlineStyle?.cssProperties?.length) { - lines.push('/* Inline style */'); - lines.push('element {'); - for (const prop of matched.inlineStyle.cssProperties) { - if (prop.name && prop.value) { - lines.push(` ${prop.name}: ${prop.value};`); - } - } - lines.push('}\n'); - } - - // matched - if (matched.matchedCSSRules?.length) { - for (const ruleEntry of matched.matchedCSSRules) { - const rule = ruleEntry.rule; - const selectors = rule.selectorList.selectors.map((s: any) => s.text).join(', '); - lines.push(`/* Matched Rule from ${rule.origin} */`); - lines.push(`${selectors} {`); - for (const prop of rule.style.cssProperties) { - if (prop.name && prop.value) { - lines.push(` ${prop.name}: ${prop.value};`); - } - } - lines.push('}\n'); - } - } - - // inherited rules - if (matched.inherited?.length) { - let level = 1; - for (const inherited of matched.inherited) { - const rules = inherited.matchedCSSRules || []; - for (const ruleEntry of rules) { - const rule = ruleEntry.rule; - const selectors = rule.selectorList.selectors.map((s: any) => s.text).join(', '); - lines.push(`/* Inherited from ancestor level ${level} (${rule.origin}) */`); - lines.push(`${selectors} {`); - for (const prop of rule.style.cssProperties) { - if (prop.name && prop.value) { - lines.push(` ${prop.name}: ${prop.value};`); - } - } - lines.push('}\n'); - } - level++; - } - } - - return '\n' + lines.join('\n'); - } - //#endregion diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/simpleBrowserEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/simpleBrowserEditorOverlay.ts index 3b30754c8ca..1d8f05a8739 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/simpleBrowserEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/simpleBrowserEditorOverlay.ts @@ -33,6 +33,7 @@ import { URI } from '../../../../../base/common/uri.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { IChatRequestVariableEntry } from '../../common/chatModel.js'; import { IPreferencesService } from '../../../../services/preferences/common/preferences.js'; +import { IBrowserElementsService } from '../../../../services/browserElements/browser/browserElementsService.js'; class SimpleBrowserOverlayWidget { @@ -55,6 +56,7 @@ class SimpleBrowserOverlayWidget { @ILogService private readonly logService: ILogService, @IConfigurationService private readonly configurationService: IConfigurationService, @IPreferencesService private readonly _preferencesService: IPreferencesService, + @IBrowserElementsService private readonly _browserElementsService: IBrowserElementsService, ) { this._showStore.add(this.configurationService.onDidChangeConfiguration(e => { @@ -183,7 +185,8 @@ class SimpleBrowserOverlayWidget { async addElementToChat(cts: CancellationTokenSource) { const editorContainer = this._container.querySelector('.editor-container') as HTMLDivElement; const editorContainerPosition = editorContainer ? editorContainer.getBoundingClientRect() : this._container.getBoundingClientRect(); - const elementData = await this._hostService.getElementData(editorContainerPosition, cts.token); + + const elementData = await this._browserElementsService.getElementData(editorContainerPosition, cts.token); if (!elementData) { throw new Error('Element data not found'); } diff --git a/src/vs/workbench/services/browserElements/browser/browserElementsService.ts b/src/vs/workbench/services/browserElements/browser/browserElementsService.ts new file mode 100644 index 00000000000..32a4d074c8f --- /dev/null +++ b/src/vs/workbench/services/browserElements/browser/browserElementsService.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { IElementData } from '../../../../platform/browserElements/common/browserElements.js'; +import { IRectangle } from '../../../../platform/window/common/window.js'; + +export const IBrowserElementsService = createDecorator('browserElementsService'); + +export interface IBrowserElementsService { + _serviceBrand: undefined; + + // no browser implementation yet + getElementData(rect: IRectangle, token: CancellationToken, cancellationId?: number): Promise; +} diff --git a/src/vs/workbench/services/browserElements/electron-sandbox/browserElementsService.ts b/src/vs/workbench/services/browserElements/electron-sandbox/browserElementsService.ts new file mode 100644 index 00000000000..cdef9e183d9 --- /dev/null +++ b/src/vs/workbench/services/browserElements/electron-sandbox/browserElementsService.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IElementData, INativeBrowserElementsService } from '../../../../platform/browserElements/common/browserElements.js'; +import { IRectangle } from '../../../../platform/window/common/window.js'; +import { ipcRenderer } from '../../../../base/parts/sandbox/electron-sandbox/globals.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; +import { IBrowserElementsService } from '../browser/browserElementsService.js'; +import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; +import { INativeWorkbenchEnvironmentService } from '../../environment/electron-sandbox/environmentService.js'; +import { NativeBrowserElementsService } from '../../../../platform/browserElements/common/nativeBrowserElementsService.js'; + +class WorkbenchNativeBrowserElementsService extends NativeBrowserElementsService { + + constructor( + @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, + @IMainProcessService mainProcessService: IMainProcessService + ) { + super(environmentService.window.id, mainProcessService); + } +} + +let cancelSelectionIdPool = 0; + +class WorkbenchBrowserElementsService implements IBrowserElementsService { + _serviceBrand: undefined; + + constructor( + @INativeBrowserElementsService private readonly simpleBrowser: INativeBrowserElementsService + ) { } + + async getElementData(rect: IRectangle, token: CancellationToken): Promise { + const cancelSelectionId = cancelSelectionIdPool++; + const onCancelChannel = `vscode:cancelElementSelection${cancelSelectionId}`; + const disposable = token.onCancellationRequested(() => { + ipcRenderer.send(onCancelChannel, cancelSelectionId); + }); + try { + const elementData = await this.simpleBrowser.getElementData(rect, token, cancelSelectionId); + return elementData; + } catch (error) { + disposable.dispose(); + throw new Error(`Native Host: Error getting element data: ${error}`); + } finally { + disposable.dispose(); + } + } +} + +registerSingleton(IBrowserElementsService, WorkbenchBrowserElementsService, InstantiationType.Delayed); +registerSingleton(INativeBrowserElementsService, WorkbenchNativeBrowserElementsService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 66f4acaa3b0..a0a5de5a6bc 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -42,7 +42,6 @@ import { isIOS, isMacintosh } from '../../../../base/common/platform.js'; import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; import { URI } from '../../../../base/common/uri.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; -import { IElementData } from '../../../../platform/native/common/native.js'; enum HostShutdownReason { @@ -652,10 +651,6 @@ export class BrowserHostService extends Disposable implements IHostService { } } - async getElementData(): Promise { - return undefined; - } - async getBrowserId(): Promise { return undefined; } diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index c3a7161c2c6..964816da080 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -4,10 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from '../../../../base/common/buffer.js'; -import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Event } from '../../../../base/common/event.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IElementData } from '../../../../platform/native/common/native.js'; import { IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions, IPoint, IRectangle } from '../../../../platform/window/common/window.js'; export const IHostService = createDecorator('hostService'); @@ -132,8 +130,6 @@ export interface IHostService { */ getScreenshot(rect?: IRectangle): Promise; - getElementData(rect: IRectangle, token: CancellationToken,): Promise; - //#endregion //#region Native Handle diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index 74c46564ae3..4a8f261d50d 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { IHostService } from '../browser/host.js'; -import { IElementData, INativeHostService } from '../../../../platform/native/common/native.js'; +import { INativeHostService } from '../../../../platform/native/common/native.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { ILabelService, Verbosity } from '../../../../platform/label/common/label.js'; import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js'; @@ -18,8 +18,6 @@ import { disposableWindowInterval, getActiveDocument, getWindowId, getWindowsCou import { memoize } from '../../../../base/common/decorators.js'; import { isAuxiliaryWindow } from '../../../../base/browser/window.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; -import { CancellationToken } from '../../../../base/common/cancellation.js'; -import { ipcRenderer } from '../../../../base/parts/sandbox/electron-sandbox/globals.js'; class WorkbenchNativeHostService extends NativeHostService { @@ -31,8 +29,6 @@ class WorkbenchNativeHostService extends NativeHostService { } } -let cancelSelectionIdPool = 0; - class WorkbenchHostService extends Disposable implements IHostService { declare readonly _serviceBrand: undefined; @@ -201,23 +197,6 @@ class WorkbenchHostService extends Disposable implements IHostService { return this.nativeHostService.getScreenshot(rect); } - async getElementData(rect: IRectangle, token: CancellationToken,): Promise { - const cancelSelectionId = cancelSelectionIdPool++; - const onCancelChannel = `vscode:cancelElementSelection${cancelSelectionId}`; - const disposable = token.onCancellationRequested(() => { - ipcRenderer.send(onCancelChannel, cancelSelectionId); - }); - try { - const elementData = await this.nativeHostService.getElementData(rect, token, cancelSelectionId); - return elementData; - } catch (error) { - disposable.dispose(); - throw new Error(`Native Host: Error getting element data: ${error}`); - } finally { - disposable.dispose(); - } - } - //#endregion //#region Native Handle diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 293bec7e685..64146b353b5 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -183,7 +183,6 @@ import { IHoverService } from '../../../platform/hover/browser/hover.js'; import { NullHoverService } from '../../../platform/hover/test/browser/nullHoverService.js'; import { IActionViewItemService, NullActionViewItemService } from '../../../platform/actions/browser/actionViewItemService.js'; import { IMarkdownString } from '../../../base/common/htmlContent.js'; -import { IElementData } from '../../../platform/native/common/native.js'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -1579,7 +1578,6 @@ export class TestHostService implements IHostService { async toggleFullScreen(): Promise { } async getScreenshot(rect?: IRectangle): Promise { return undefined; } - async getElementData(rect: IRectangle, token: CancellationToken,): Promise { return undefined; } async getNativeWindowHandle(_windowId: number): Promise { return undefined; } diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index 4358257f5aa..e7d0cb93e36 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -6,7 +6,7 @@ import { Event } from '../../../base/common/event.js'; import { workbenchInstantiationService as browserWorkbenchInstantiationService, ITestInstantiationService, TestEncodingOracle, TestEnvironmentService, TestFileDialogService, TestFilesConfigurationService, TestFileService, TestLifecycleService, TestTextFileService } from '../browser/workbenchTestServices.js'; import { ISharedProcessService } from '../../../platform/ipc/electron-sandbox/services.js'; -import { INativeHostService, INativeHostOptions, IOSProperties, IOSStatistics, IElementData } from '../../../platform/native/common/native.js'; +import { INativeHostService, INativeHostOptions, IOSProperties, IOSStatistics } from '../../../platform/native/common/native.js'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from '../../../base/common/buffer.js'; import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js'; import { URI } from '../../../base/common/uri.js'; @@ -167,7 +167,6 @@ export class TestNativeHostService implements INativeHostService { async windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise { return undefined; } async profileRenderer(): Promise { throw new Error(); } async getScreenshot(rect?: IRectangle): Promise { return undefined; } - async getElementData(rect: IRectangle, token: CancellationToken, cancellationId?: number): Promise { return undefined; } } export class TestExtensionTipsService extends AbstractNativeExtensionTipsService { diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index e7c40ead98b..3d4ab2566a9 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -54,6 +54,7 @@ import './services/path/electron-sandbox/pathService.js'; import './services/themes/electron-sandbox/nativeHostColorSchemeService.js'; import './services/extensionManagement/electron-sandbox/extensionManagementService.js'; import './services/encryption/electron-sandbox/encryptionService.js'; +import './services/browserElements/electron-sandbox/browserElementsService.js'; import './services/secrets/electron-sandbox/secretStorageService.js'; import './services/localization/electron-sandbox/languagePackService.js'; import './services/telemetry/electron-sandbox/telemetryService.js'; diff --git a/src/vs/workbench/workbench.web.main.internal.ts b/src/vs/workbench/workbench.web.main.internal.ts index ca91b124d63..f9d87eb5af2 100644 --- a/src/vs/workbench/workbench.web.main.internal.ts +++ b/src/vs/workbench/workbench.web.main.internal.ts @@ -67,6 +67,7 @@ import './services/userDataProfile/browser/userDataProfileStorageService.js'; import './services/configurationResolver/browser/configurationResolverService.js'; import '../platform/extensionResourceLoader/browser/extensionResourceLoaderService.js'; import './services/auxiliaryWindow/browser/auxiliaryWindowService.js'; +import './services/browserElements/browser/browserElementsService.js'; import { InstantiationType, registerSingleton } from '../platform/instantiation/common/extensions.js'; import { IAccessibilityService } from '../platform/accessibility/common/accessibility.js'; From ea0483469b9f86986f35af2d436af65f37d969b9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 May 2025 10:41:18 +0200 Subject: [PATCH 64/90] ESM improvements for hybrid extensions (#248286) This PR makes it possible to have an extension that uses ESM for the NodeJS EH and CJS for the web worker EH. A sample is the GH issue notebooks extension: https://github.com/microsoft/vscode-github-issue-notebooks/pull/185 re https://github.com/microsoft/vscode/issues/130367 --- src/vs/workbench/api/common/extHostExtensionService.ts | 2 +- src/vs/workbench/api/worker/extHostExtensionService.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 5b14b6d5a37..dbaf5bbb595 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -1087,7 +1087,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme } protected _isESM(extensionDescription: IExtensionDescription | undefined, modulePath?: string): boolean { - modulePath ??= extensionDescription?.main; + modulePath ??= extensionDescription ? this._getEntryPoint(extensionDescription) : modulePath; return modulePath?.endsWith('.mjs') || (extensionDescription?.type === 'module' && !modulePath?.endsWith('.cjs')); } diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 46bb2a0970f..60584ef78a9 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -12,6 +12,7 @@ import { IExtensionDescription } from '../../../platform/extensions/common/exten import { ExtensionRuntime } from '../common/extHostTypes.js'; import { timeout } from '../../../base/common/async.js'; import { ExtHostConsoleForwarder } from './extHostConsoleForwarder.js'; +import { extname } from '../../../base/common/path.js'; class WorkerRequireInterceptor extends RequireInterceptor { @@ -147,5 +148,6 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } function ensureSuffix(path: string, suffix: string): string { - return path.endsWith(suffix) ? path : path + suffix; + const extName = extname(path); + return extName ? path : path + suffix; } From 12add8b5e2e30af1d115bc0bd53e4e0395c06de6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 7 May 2025 11:49:05 +0200 Subject: [PATCH 65/90] switch default to use marketplace latest api (#248289) --- .../extensionManagement/common/extensionGalleryService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 7dbac890425..c6efadb3ccc 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -637,7 +637,7 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle }; } - const value = await this.assignmentService?.getTreatment<'unpkg' | 'marketplace' | 'none'>('extensions.gallery.useResourceApi') ?? 'unpkg'; + const value = await this.assignmentService?.getTreatment<'unpkg' | 'marketplace' | 'none'>('extensions.gallery.useResourceApi') ?? 'marketplace'; if (value === 'marketplace') { return { From 503bace89cb48c7ef8cbee8109e262889bf99907 Mon Sep 17 00:00:00 2001 From: M Hickford Date: Wed, 7 May 2025 10:55:58 +0100 Subject: [PATCH 66/90] Highlight active line number correctly regardless of word wrap (#240029) Previously, the line number was only highlighted when cursor was on the first part of a wrapped line. --- .../browser/viewParts/lineNumbers/lineNumbers.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts index ee6461fd7f8..31234a177af 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -32,7 +32,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { private _lineNumbersWidth!: number; private _lastCursorModelPosition: Position; private _renderResult: string[] | null; - private _activeLineNumber: number; + private _activeModelLineNumber: number; constructor(context: ViewContext) { super(); @@ -42,7 +42,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { this._lastCursorModelPosition = new Position(1, 1); this._renderResult = null; - this._activeLineNumber = 1; + this._activeModelLineNumber = 1; this._context.addEventHandler(this); } @@ -75,8 +75,8 @@ export class LineNumbersOverlay extends DynamicViewOverlay { this._lastCursorModelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(primaryViewPosition); let shouldRender = false; - if (this._activeLineNumber !== primaryViewPosition.lineNumber) { - this._activeLineNumber = primaryViewPosition.lineNumber; + if (this._activeModelLineNumber !== this._lastCursorModelPosition.lineNumber) { + this._activeModelLineNumber = this._lastCursorModelPosition.lineNumber; shouldRender = true; } if (this._renderLineNumbers === RenderLineNumbersType.Relative || this._renderLineNumbers === RenderLineNumbersType.Interval) { @@ -162,6 +162,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { const lineIndex = lineNumber - visibleStartLineNumber; + const modelLineNumber: number = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, 1)).lineNumber; let renderLineNumber = this._getLineRenderLineNumber(lineNumber); let extraClassNames = ''; @@ -191,7 +192,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay { extraClassNames += ' dimmed-line-number'; } } - if (lineNumber === this._activeLineNumber) { + if (modelLineNumber === this._activeModelLineNumber) { extraClassNames += ' active-line-number'; } From b7a4e2f7106e8d610d96c53549ccc48f3d84c3ac Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 11:52:52 +0200 Subject: [PATCH 67/90] Adds Edit/StringEdit/ArrayEdit/LengthEdit data structures --- src/vs/base/common/arrays.ts | 8 + src/vs/editor/common/core/edits/arrayEdit.ts | 90 ++++++ .../core/edits/docs/BaseEdit_compose.dio.svg | 91 ++++++ .../edits/docs/BaseEdit_normalize.dio.svg | 142 +++++++++ .../core/edits/docs/BaseEdit_rebase.dio.svg | 124 ++++++++ src/vs/editor/common/core/edits/edit.ts | 284 ++++++++++++++++++ src/vs/editor/common/core/edits/lengthEdit.ts | 52 ++++ src/vs/editor/common/core/edits/stringEdit.ts | 90 ++++++ src/vs/editor/common/core/offsetEdit.ts | 10 +- src/vs/editor/common/core/offsetRange.ts | 13 +- src/vs/editor/test/common/core/edit.test.ts | 192 ++++++++++++ src/vs/editor/test/common/core/random.ts | 78 ++++- 12 files changed, 1156 insertions(+), 18 deletions(-) create mode 100644 src/vs/editor/common/core/edits/arrayEdit.ts create mode 100644 src/vs/editor/common/core/edits/docs/BaseEdit_compose.dio.svg create mode 100644 src/vs/editor/common/core/edits/docs/BaseEdit_normalize.dio.svg create mode 100644 src/vs/editor/common/core/edits/docs/BaseEdit_rebase.dio.svg create mode 100644 src/vs/editor/common/core/edits/edit.ts create mode 100644 src/vs/editor/common/core/edits/lengthEdit.ts create mode 100644 src/vs/editor/common/core/edits/stringEdit.ts create mode 100644 src/vs/editor/test/common/core/edit.test.ts diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 7efd1f97fc9..ff00c68b040 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -942,3 +942,11 @@ export async function findAsync(array: readonly T[], predicate: (element: T, return results.find(r => r.ok)?.element; } + +export function sum(array: readonly number[]): number { + return array.reduce((acc, value) => acc + value, 0); +} + +export function sumBy(array: readonly T[], selector: (value: T) => number): number { + return array.reduce((acc, value) => acc + selector(value), 0); +} diff --git a/src/vs/editor/common/core/edits/arrayEdit.ts b/src/vs/editor/common/core/edits/arrayEdit.ts new file mode 100644 index 00000000000..ab88e206792 --- /dev/null +++ b/src/vs/editor/common/core/edits/arrayEdit.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OffsetRange } from '../offsetRange.js'; +import { BaseEdit, BaseReplacement } from './edit.js'; + +/** + * Represents a set of replacements to an array. + * All these replacements are applied at once. +*/ +export class ArrayEdit extends BaseEdit, ArrayEdit> { + public static readonly empty = new ArrayEdit([]); + + public static create(replacements: readonly ArrayReplacement[]): ArrayEdit { + return new ArrayEdit(replacements); + } + + public static single(replacement: ArrayReplacement): ArrayEdit { + return new ArrayEdit([replacement]); + } + + public static replace(range: OffsetRange, replacement: readonly T[]): ArrayEdit { + return new ArrayEdit([new ArrayReplacement(range, replacement)]); + } + + public static insert(offset: number, replacement: readonly T[]): ArrayEdit { + return new ArrayEdit([new ArrayReplacement(OffsetRange.emptyAt(offset), replacement)]); + } + + public static delete(range: OffsetRange): ArrayEdit { + return new ArrayEdit([new ArrayReplacement(range, [])]); + } + + override _createNew(replacements: readonly ArrayReplacement[]): ArrayEdit { + return new ArrayEdit(replacements); + } + + public apply(data: readonly T[]): readonly T[] { + const resultData: T[] = []; + let pos = 0; + for (const edit of this.replacements) { + resultData.push(...data.slice(pos, edit.replaceRange.start)); + resultData.push(...edit.newValue); + pos = edit.replaceRange.endExclusive; + } + resultData.push(...data.slice(pos)); + return resultData; + } + + /** + * Creates an edit that reverts this edit. + */ + public inverse(baseVal: readonly T[]): ArrayEdit { + const edits: ArrayReplacement[] = []; + let offset = 0; + for (const e of this.replacements) { + edits.push(new ArrayReplacement( + OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.newValue.length), + baseVal.slice(e.replaceRange.start, e.replaceRange.endExclusive), + )); + offset += e.newValue.length - e.replaceRange.length; + } + return new ArrayEdit(edits); + } +} + +export class ArrayReplacement extends BaseReplacement> { + constructor( + range: OffsetRange, + public readonly newValue: readonly T[] + ) { + super(range); + } + + override equals(other: ArrayReplacement): boolean { + return this.replaceRange.equals(other.replaceRange) && this.newValue.length === other.newValue.length && this.newValue.every((v, i) => v === other.newValue[i]); + } + + getNewLength(): number { return this.newValue.length; } + + tryJoinTouching(other: ArrayReplacement): ArrayReplacement | undefined { + return new ArrayReplacement(this.replaceRange.joinRightTouching(other.replaceRange), this.newValue.concat(other.newValue)); + } + + slice(range: OffsetRange, rangeInReplacement: OffsetRange): ArrayReplacement { + return new ArrayReplacement(range, rangeInReplacement.slice(this.newValue)); + } +} diff --git a/src/vs/editor/common/core/edits/docs/BaseEdit_compose.dio.svg b/src/vs/editor/common/core/edits/docs/BaseEdit_compose.dio.svg new file mode 100644 index 00000000000..516defacf48 --- /dev/null +++ b/src/vs/editor/common/core/edits/docs/BaseEdit_compose.dio.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + +
+
+
+ this +
+
+
+
+ + this + +
+
+
+ + + + + + + + +
+
+
+ this.compose(other) +
+
+
+
+ + this.compose(other) + +
+
+
+ + + + + + + + + + + +
+
+
+ other +
+
+
+
+ + other + +
+
+
+ + + + + + + + + +
+ + + + + Text is not SVG - cannot display + + + +
diff --git a/src/vs/editor/common/core/edits/docs/BaseEdit_normalize.dio.svg b/src/vs/editor/common/core/edits/docs/BaseEdit_normalize.dio.svg new file mode 100644 index 00000000000..36b8d5d329f --- /dev/null +++ b/src/vs/editor/common/core/edits/docs/BaseEdit_normalize.dio.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + +
+
+
+ this +
+
+
+
+ + this + +
+
+
+ + + + + + + +
+
+
+ * +
+
+
+
+ + * + +
+
+
+ + + + + + + + +
+
+
+ this.normalize() +
+
+
+
+ + this.normalize() + +
+
+
+ + + + +
+
+
+ .equals(other.normalize()) +
+
+
+
+ + .equals(other.normalize()) + +
+
+
+ + + + + + + +
+
+
+ * +
+
+
+
+ + * + +
+
+
+ + + + + + + + + + + +
+
+
+ other +
+
+
+
+ + other + +
+
+
+
+ + + + + Text is not SVG - cannot display + + + +
diff --git a/src/vs/editor/common/core/edits/docs/BaseEdit_rebase.dio.svg b/src/vs/editor/common/core/edits/docs/BaseEdit_rebase.dio.svg new file mode 100644 index 00000000000..8d096e6500e --- /dev/null +++ b/src/vs/editor/common/core/edits/docs/BaseEdit_rebase.dio.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + +
+
+
+ e1 +
+
+
+
+ + e1 + +
+
+
+ + + + + + + + + + + + + + + +
+
+
+ e2_ +
+
+
+
+ + e2_ + +
+
+
+ + + + + + + + + + +
+
+
+ e2 +
+
+
+
+ + e2 + +
+
+
+ + + + + + + + +
+
+
+ + e1_ + +
+
+
+
+ + e1_ + +
+
+
+ + + + + + + + + + + + +
+ + + + + Text is not SVG - cannot display + + + +
diff --git a/src/vs/editor/common/core/edits/edit.ts b/src/vs/editor/common/core/edits/edit.ts new file mode 100644 index 00000000000..28ba49030ce --- /dev/null +++ b/src/vs/editor/common/core/edits/edit.ts @@ -0,0 +1,284 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { sumBy } from '../../../../base/common/arrays.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetRange } from '../offsetRange.js'; + +export abstract class BaseEdit, TEdit extends BaseEdit> { + protected constructor( + public readonly replacements: readonly T[], + ) { + let lastEndEx = -1; + for (const replacement of replacements) { + if (!(replacement.replaceRange.start >= lastEndEx)) { + throw new BugIndicatingError(`Edits must be disjoint and sorted. Found ${replacement} after ${lastEndEx}`); + } + lastEndEx = replacement.replaceRange.endExclusive; + } + } + + protected abstract _createNew(replacements: readonly T[]): TEdit; + + public equals(other: TEdit): boolean { + if (this.replacements.length !== other.replacements.length) { + return false; + } + for (let i = 0; i < this.replacements.length; i++) { + if (!this.replacements[i].equals(other.replacements[i])) { + return false; + } + } + return true; + } + + public toString() { + const edits = this.replacements.map(e => e.toString()).join(', '); + return `[${edits}]`; + } + + /** + * Normalizes the edit by removing empty replacements and joining touching replacements (if the replacements allow joining). + * Two edits have an equal normalized edit if and only if they have the same effect on any input. + * + * ![](./docs/BaseEdit_normalize.dio.svg) + * + * Invariant: + * ``` + * (forall base: TEdit.apply(base).equals(other.apply(base))) <-> this.normalize().equals(other.normalize()) + * ``` + * and + * ``` + * forall base: TEdit.apply(base).equals(this.normalize().apply(base)) + * ``` + * + */ + public normalize(): TEdit { + const newReplacements: T[] = []; + let lastReplacement: T | undefined; + for (const r of this.replacements) { + if (r.getNewLength() === 0 && r.replaceRange.length === 0) { + continue; + } + if (lastReplacement && lastReplacement.replaceRange.endExclusive === r.replaceRange.start) { + const joined = lastReplacement.tryJoinTouching(r); + if (joined) { + lastReplacement = joined; + continue; + } + } + + if (lastReplacement) { + newReplacements.push(lastReplacement); + } + lastReplacement = r; + } + + if (lastReplacement) { + newReplacements.push(lastReplacement); + } + return this._createNew(newReplacements); + } + + /** + * Combines two edits into one with the same effect. + * + * ![](./docs/BaseEdit_compose.dio.svg) + * + * Invariant: + * ``` + * other.apply(this.apply(s0)) = this.compose(other).apply(s0) + * ``` + */ + public compose(other: TEdit): TEdit { + const edits1 = this.normalize(); + const edits2 = other.normalize(); + + if (edits1.isEmpty()) { return edits2; } + if (edits2.isEmpty()) { return edits1; } + + const edit1Queue = [...edits1.replacements]; + const result: T[] = []; + + let edit1ToEdit2 = 0; + + for (const r2 of edits2.replacements) { + // Copy over edit1 unmodified until it touches edit2. + while (true) { + const r1 = edit1Queue[0]!; + if (!r1 || r1.replaceRange.start + edit1ToEdit2 + r1.getNewLength() >= r2.replaceRange.start) { + break; + } + edit1Queue.shift(); + + result.push(r1); + edit1ToEdit2 += r1.getNewLength() - r1.replaceRange.length; + } + + const firstEdit1ToEdit2 = edit1ToEdit2; + let firstIntersecting: T | undefined; // or touching + let lastIntersecting: T | undefined; // or touching + + while (true) { + const r1 = edit1Queue[0]; + if (!r1 || r1.replaceRange.start + edit1ToEdit2 > r2.replaceRange.endExclusive) { + break; + } + // else we intersect, because the new end of edit1 is after or equal to our start + + if (!firstIntersecting) { + firstIntersecting = r1; + } + lastIntersecting = r1; + edit1Queue.shift(); + + edit1ToEdit2 += r1.getNewLength() - r1.replaceRange.length; + } + + if (!firstIntersecting) { + result.push(r2.delta(-edit1ToEdit2)); + } else { + const newReplaceRangeStart = Math.min(firstIntersecting.replaceRange.start, r2.replaceRange.start - firstEdit1ToEdit2); + + const prefixLength = r2.replaceRange.start - (firstIntersecting.replaceRange.start + firstEdit1ToEdit2); + if (prefixLength > 0) { + const prefix = firstIntersecting.slice(OffsetRange.emptyAt(newReplaceRangeStart), new OffsetRange(0, prefixLength)); + result.push(prefix); + } + if (!lastIntersecting) { + throw new BugIndicatingError(`Invariant violation: lastIntersecting is undefined`); + } + const suffixLength = (lastIntersecting.replaceRange.endExclusive + edit1ToEdit2) - r2.replaceRange.endExclusive; + if (suffixLength > 0) { + const e = lastIntersecting.slice( + OffsetRange.ofStartAndLength(lastIntersecting.replaceRange.endExclusive, 0), + new OffsetRange(lastIntersecting.getNewLength() - suffixLength, lastIntersecting.getNewLength()) + ); + edit1Queue.unshift(e); + edit1ToEdit2 -= e.getNewLength() - e.replaceRange.length; + } + + const newReplaceRange = new OffsetRange( + newReplaceRangeStart, + r2.replaceRange.endExclusive - edit1ToEdit2 + ); + const middle = r2.slice(newReplaceRange, new OffsetRange(0, r2.getNewLength())); + result.push(middle); + } + } + + while (true) { + const item = edit1Queue.shift(); + if (!item) { break; } + result.push(item); + } + + return this._createNew(result).normalize(); + } + + public getNewRanges(): OffsetRange[] { + const ranges: OffsetRange[] = []; + let offset = 0; + for (const e of this.replacements) { + ranges.push(OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.getNewLength())); + offset += e.getLengthDelta(); + } + return ranges; + } + + public getJoinedReplaceRange(): OffsetRange | undefined { + if (this.replacements.length === 0) { + return undefined; + } + return this.replacements[0].replaceRange.join(this.replacements.at(-1)!.replaceRange); + } + + public isEmpty(): boolean { + return this.replacements.length === 0; + } + + public getLengthDelta(): number { + return sumBy(this.replacements, (replacement) => replacement.getLengthDelta()); + } +} + +export abstract class BaseReplacement> { + constructor( + /** + * The range to be replaced. + */ + public readonly replaceRange: OffsetRange, + ) { } + + public abstract getNewLength(): number; + + /** + * Precondition: TEdit.range.endExclusive === other.range.start + */ + public abstract tryJoinTouching(other: TSelf): TSelf | undefined; + + public abstract slice(newReplaceRange: OffsetRange, rangeInReplacement: OffsetRange): TSelf; + + public delta(offset: number): TSelf { + return this.slice(this.replaceRange.delta(offset), new OffsetRange(0, this.getNewLength())); + } + + public getLengthDelta(): number { + return this.getNewLength() - this.replaceRange.length; + } + + abstract equals(other: TSelf): boolean; + + toString(): string { + return `{ ${this.replaceRange.toString()} -> ${this.getNewLength()} }`; + } +} + +export class Edit> extends BaseEdit> { + /** + * Represents a set of edits to a string. + * All these edits are applied at once. + */ + public static readonly empty = new Edit([]); + + public static create>(replacements: readonly T[]): Edit { + return new Edit(replacements); + } + + public static single>(replacement: T): Edit { + return new Edit([replacement]); + } + + override _createNew(replacements: readonly T[]): Edit { + return new Edit(replacements); + } +} + +export class AnnotationReplacement extends BaseReplacement> { + constructor( + range: OffsetRange, + public readonly newLength: number, + public readonly annotation: TAnnotation, + ) { + super(range); + } + + override equals(other: AnnotationReplacement): boolean { + return this.replaceRange.equals(other.replaceRange) && this.newLength === other.newLength && this.annotation === other.annotation; + } + + getNewLength(): number { return this.newLength; } + + tryJoinTouching(other: AnnotationReplacement): AnnotationReplacement | undefined { + if (this.annotation !== other.annotation) { + return undefined; + } + return new AnnotationReplacement(this.replaceRange.joinRightTouching(other.replaceRange), this.newLength + other.newLength, this.annotation); + } + + slice(range: OffsetRange, rangeInReplacement: OffsetRange): AnnotationReplacement { + return new AnnotationReplacement(range, rangeInReplacement.length, this.annotation); + } +} diff --git a/src/vs/editor/common/core/edits/lengthEdit.ts b/src/vs/editor/common/core/edits/lengthEdit.ts new file mode 100644 index 00000000000..c8cad96f43c --- /dev/null +++ b/src/vs/editor/common/core/edits/lengthEdit.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OffsetRange } from '../offsetRange.js'; +import { BaseEdit, BaseReplacement } from './edit.js'; + +export class LengthEdit extends BaseEdit { + /** + * Creates an edit that reverts this edit. + */ + public inverse(): LengthEdit { + const edits: LengthReplacement[] = []; + let offset = 0; + for (const e of this.replacements) { + edits.push(new LengthReplacement( + OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.newLength), + e.replaceRange.length, + )); + offset += e.newLength - e.replaceRange.length; + } + return new LengthEdit(edits); + } + + override _createNew(replacements: readonly LengthReplacement[]): LengthEdit { + return new LengthEdit(replacements); + } +} + +export class LengthReplacement extends BaseReplacement { + constructor( + range: OffsetRange, + public readonly newLength: number, + ) { + super(range); + } + + override equals(other: LengthReplacement): boolean { + return this.replaceRange.equals(other.replaceRange) && this.newLength === other.newLength; + } + + getNewLength(): number { return this.newLength; } + + tryJoinTouching(other: LengthReplacement): LengthReplacement | undefined { + return new LengthReplacement(this.replaceRange.joinRightTouching(other.replaceRange), this.newLength + other.newLength); + } + + slice(range: OffsetRange, rangeInReplacement: OffsetRange): LengthReplacement { + return new LengthReplacement(range, rangeInReplacement.length); + } +} diff --git a/src/vs/editor/common/core/edits/stringEdit.ts b/src/vs/editor/common/core/edits/stringEdit.ts new file mode 100644 index 00000000000..9b816307853 --- /dev/null +++ b/src/vs/editor/common/core/edits/stringEdit.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OffsetRange } from '../offsetRange.js'; +import { BaseEdit, BaseReplacement } from './edit.js'; + +/** + * Represents a set of replacements to a string. + * All these replacements are applied at once. +*/ +export class StringEdit extends BaseEdit { + public static readonly empty = new StringEdit([]); + + public static create(replacements: readonly StringReplacement[]): StringEdit { + return new StringEdit(replacements); + } + + public static single(replacement: StringReplacement): StringEdit { + return new StringEdit([replacement]); + } + + public static replace(range: OffsetRange, replacement: string): StringEdit { + return new StringEdit([new StringReplacement(range, replacement)]); + } + + public static insert(offset: number, replacement: string): StringEdit { + return new StringEdit([new StringReplacement(OffsetRange.emptyAt(offset), replacement)]); + } + + public static delete(range: OffsetRange): StringEdit { + return new StringEdit([new StringReplacement(range, '')]); + } + + override _createNew(replacements: readonly StringReplacement[]): StringEdit { + return new StringEdit(replacements); + } + + public apply(base: string): string { + const resultText: string[] = []; + let pos = 0; + for (const edit of this.replacements) { + resultText.push(base.substring(pos, edit.replaceRange.start)); + resultText.push(edit.newValue); + pos = edit.replaceRange.endExclusive; + } + resultText.push(base.substring(pos)); + return resultText.join(''); + } + + /** + * Creates an edit that reverts this edit. + */ + public inverse(baseStr: string): StringEdit { + const edits: StringReplacement[] = []; + let offset = 0; + for (const e of this.replacements) { + edits.push(new StringReplacement( + OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.newValue.length), + baseStr.substring(e.replaceRange.start, e.replaceRange.endExclusive), + )); + offset += e.newValue.length - e.replaceRange.length; + } + return new StringEdit(edits); + } +} + +export class StringReplacement extends BaseReplacement { + constructor( + range: OffsetRange, + public readonly newValue: string, + ) { + super(range); + } + + override equals(other: StringReplacement): boolean { + return this.replaceRange.equals(other.replaceRange) && this.newValue === other.newValue; + } + + getNewLength(): number { return this.newValue.length; } + + tryJoinTouching(other: StringReplacement): StringReplacement | undefined { + return new StringReplacement(this.replaceRange.joinRightTouching(other.replaceRange), this.newValue + other.newValue); + } + + slice(range: OffsetRange, rangeInReplacement: OffsetRange): StringReplacement { + return new StringReplacement(range, rangeInReplacement.substring(this.newValue)); + } +} diff --git a/src/vs/editor/common/core/offsetEdit.ts b/src/vs/editor/common/core/offsetEdit.ts index adcbe4da86d..091dadb84e8 100644 --- a/src/vs/editor/common/core/offsetEdit.ts +++ b/src/vs/editor/common/core/offsetEdit.ts @@ -28,17 +28,11 @@ export class OffsetEdit { return new OffsetEdit(data.map(SingleOffsetEdit.fromJson)); } - public static replace( - range: OffsetRange, - newText: string, - ): OffsetEdit { + public static replace(range: OffsetRange, newText: string): OffsetEdit { return new OffsetEdit([new SingleOffsetEdit(range, newText)]); } - public static insert( - offset: number, - insertText: string, - ): OffsetEdit { + public static insert(offset: number, insertText: string): OffsetEdit { return OffsetEdit.replace(OffsetRange.emptyAt(offset), insertText); } diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index 4786748450d..975dddc1eaf 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -146,7 +146,7 @@ export class OffsetRange implements IOffsetRange { return this.start >= other.endExclusive; } - public slice(arr: T[]): T[] { + public slice(arr: readonly T[]): T[] { return arr.slice(this.start, this.endExclusive); } @@ -197,6 +197,17 @@ export class OffsetRange implements IOffsetRange { f(i); } } + + /** + * this: [ 5, 10), range: [10, 15) => [5, 15)] + * Throws if the ranges are not touching. + */ + public joinRightTouching(range: OffsetRange): OffsetRange { + if (this.endExclusive !== range.start) { + throw new BugIndicatingError(`Invalid join: ${this.toString()} and ${range.toString()}`); + } + return new OffsetRange(this.start, range.endExclusive); + } } export class OffsetRangeSet { diff --git a/src/vs/editor/test/common/core/edit.test.ts b/src/vs/editor/test/common/core/edit.test.ts new file mode 100644 index 00000000000..fddd5a797cd --- /dev/null +++ b/src/vs/editor/test/common/core/edit.test.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { Random } from './random.js'; +import { StringEdit, StringReplacement } from '../../../common/core/edits/stringEdit.js'; +import { OffsetRange } from '../../../common/core/offsetRange.js'; +import { ArrayEdit, ArrayReplacement } from '../../../common/core/edits/arrayEdit.js'; + +suite('Edit', () => { + + ensureNoDisposablesAreLeakedInTestSuite(); + + suite('StringEdit', () => { + test('basic', () => { + const arr = '0123456789'; + const edit = StringEdit.replace(new OffsetRange(4, 6), 'xyz'); + const result = edit.apply(arr); + assert.deepStrictEqual(result, '0123xyz6789'); + }); + + test('inverse', () => { + for (let i = 0; i < 1000; i++) { + test('case' + i, () => { + runTest(i); + }); + } + + test.skip('fuzz', () => { + for (let i = 0; i < 1_000_000; i++) { + runTest(i); + } + }); + + function runTest(seed: number) { + const rng = Random.create(seed); + + const s0 = 'abcde\nfghij\nklmno\npqrst\n'; + + const e = getRandomEdit(s0, rng.nextIntRange(1, 4), rng); + const eInv = e.inverse(s0); + + assert.strictEqual(eInv.apply(e.apply(s0)), s0); + } + }); + + suite('compose', () => { + for (let i = 0; i < 1000; i++) { + test('case' + i, () => { + runTest(i); + }); + } + + test.skip('fuzz', () => { + for (let i = 0; i < 1_000_000; i++) { + runTest(i); + } + }); + + function runTest(seed: number) { + const rng = Random.create(seed); + + const s0 = 'abcde\nfghij\nklmno\npqrst\n'; + + const edits1 = getRandomEdit(s0, rng.nextIntRange(1, 4), rng); + const s1 = edits1.apply(s0); + + const edits2 = getRandomEdit(s1, rng.nextIntRange(1, 4), rng); + const s2 = edits2.apply(s1); + + const combinedEdits = edits1.compose(edits2); + const s2C = combinedEdits.apply(s0); + + assert.strictEqual(s2C, s2); + } + }); + + test('equals', () => { + const edit1 = StringEdit.replace(new OffsetRange(4, 6), 'xyz'); + const edit2 = StringEdit.replace(new OffsetRange(4, 6), 'xyz'); + const edit3 = StringEdit.replace(new OffsetRange(5, 6), 'xyz'); + const edit4 = StringEdit.replace(new OffsetRange(4, 6), 'xy'); + + assert.ok(edit1.equals(edit1)); + assert.ok(edit1.equals(edit2)); + assert.ok(edit2.equals(edit1)); + + assert.ok(!edit1.equals(edit3)); + assert.ok(!edit1.equals(edit4)); + }); + + test('getNewRanges', () => { + const edit = StringEdit.create([ + new StringReplacement(new OffsetRange(4, 6), 'abcde'), + new StringReplacement(new OffsetRange(7, 9), 'a'), + ]); + const ranges = edit.getNewRanges(); + assert.deepStrictEqual(ranges, [ + new OffsetRange(4, 9), + new OffsetRange(10, 11), + ]); + }); + + test('getJoinedReplaceRange', () => { + const edit = StringEdit.create([ + new StringReplacement(new OffsetRange(4, 6), 'abcde'), + new StringReplacement(new OffsetRange(7, 9), 'a'), + ]); + const range = edit.getJoinedReplaceRange(); + assert.deepStrictEqual(range, new OffsetRange(4, 9)); + }); + + test('getLengthDelta', () => { + const edit = StringEdit.create([ + new StringReplacement(new OffsetRange(4, 6), 'abcde'), + new StringReplacement(new OffsetRange(7, 9), 'a'), + ]); + const delta = edit.getLengthDelta(); + assert.strictEqual(delta, 2); + assert.strictEqual(edit.replacements[0].getLengthDelta(), 3); + assert.strictEqual(edit.replacements[1].getLengthDelta(), -1); + }); + }); + + suite('ArrayEdit', () => { + test('basic', () => { + const arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + const edit = ArrayEdit.replace(new OffsetRange(4, 6), ['x', 'y', 'z']); + const result = edit.apply(arr); + assert.deepStrictEqual(result, ['0', '1', '2', '3', 'x', 'y', 'z', '6', '7', '8', '9']); + }); + + suite('compose', () => { + for (let i = 0; i < 100; i++) { + test('case' + i, () => { + runTest(i); + }); + } + + function runTest(seed: number) { + const rng = Random.create(seed); + + const s0 = 'abcde\nfghij\nklmno\npqrst\n'; + + const e1 = getRandomEdit(s0, rng.nextIntRange(1, 4), rng); + const s1 = e1.apply(s0); + + const e2 = getRandomEdit(s1, rng.nextIntRange(1, 4), rng); + + const ae1 = ArrayEdit.create(e1.replacements.map(r => new ArrayReplacement(r.replaceRange, [...r.newValue]))); + const ae2 = ArrayEdit.create(e2.replacements.map(r => new ArrayReplacement(r.replaceRange, [...r.newValue]))); + const as0 = [...s0]; + const as1 = ae1.apply(as0); + const as2 = ae2.apply(as1); + const aCombinedEdits = ae1.compose(ae2); + + const as2C = aCombinedEdits.apply(as0); + assert.deepStrictEqual(as2, as2C); + } + }); + }); + + + function getRandomEdit(str: string, count: number, rng: Random): StringEdit { + const edits: StringReplacement[] = []; + let i = 0; + for (let j = 0; j < count; j++) { + if (i >= str.length) { + break; + } + edits.push(getRandomSingleEdit(str, i, rng)); + i = edits[j].replaceRange.endExclusive + 1; + } + return StringEdit.create(edits); + } + + function getRandomSingleEdit(str: string, rangeOffsetStart: number, rng: Random): StringReplacement { + const offsetStart = rng.nextIntRange(rangeOffsetStart, str.length); + const offsetEnd = rng.nextIntRange(offsetStart, str.length); + + const textStart = rng.nextIntRange(0, str.length); + const textLen = rng.nextIntRange(0, Math.min(7, str.length - textStart)); + + return new StringReplacement( + new OffsetRange(offsetStart, offsetEnd), + str.substring(textStart, textStart + textLen) + ); + } +}); diff --git a/src/vs/editor/test/common/core/random.ts b/src/vs/editor/test/common/core/random.ts index cbf9a5feca6..7e02e9c2a62 100644 --- a/src/vs/editor/test/common/core/random.ts +++ b/src/vs/editor/test/common/core/random.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { numberComparator } from '../../../../base/common/arrays.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetEdit, SingleOffsetEdit } from '../../../common/core/offsetEdit.js'; import { OffsetRange } from '../../../common/core/offsetRange.js'; import { Position } from '../../../common/core/position.js'; import { PositionOffsetTransformer } from '../../../common/core/positionToOffset.js'; @@ -11,25 +13,37 @@ import { Range } from '../../../common/core/range.js'; import { AbstractText, SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; export abstract class Random { - public static basicAlphabet: string = ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - public static basicAlphabetMultiline: string = ' \n\n\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + public static readonly alphabetSmallLowercase = 'abcdefgh'; + public static readonly alphabetSmallUppercase = 'ABCDEFGH'; + public static readonly alphabetLowercase = 'abcdefghijklmnopqrstuvwxyz'; + public static readonly alphabetUppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + public static readonly basicAlphabet: string = ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + public static readonly basicAlphabetMultiline: string = ' \n\n\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; public static create(seed: number): Random { return new MersenneTwister(seed); } + public stringGenerator(alphabet: string): IGenerator { + return { + next: () => { + const characterIndex = this.nextIntRange(0, alphabet.length); + return alphabet.charAt(characterIndex); + } + }; + } + public abstract nextIntRange(start: number, endExclusive: number): number; - public nextString(length: number, alphabet = Random.basicAlphabet): string { + public nextString(length: number, alphabet = this.stringGenerator(Random.basicAlphabet)): string { let randomText: string = ''; for (let i = 0; i < length; i++) { - const characterIndex = this.nextIntRange(0, alphabet.length); - randomText += alphabet.charAt(characterIndex); + randomText += alphabet.next(); } return randomText; } - public nextMultiLineString(lineCount: number, lineLengthRange: OffsetRange, alphabet = Random.basicAlphabet): string { + public nextMultiLineString(lineCount: number, lineLengthRange: OffsetRange, alphabet = this.stringGenerator(Random.basicAlphabet)): string { const lines: string[] = []; for (let i = 0; i < lineCount; i++) { const lineLength = this.nextIntRange(lineLengthRange.start, lineLengthRange.endExclusive); @@ -38,10 +52,15 @@ export abstract class Random { return lines.join('\n'); } + public nextConsecutiveOffsets(range: OffsetRange, count: number): number[] { + const offsets = OffsetRange.ofLength(count).map(() => this.nextIntRange(range.start, range.endExclusive)); + offsets.sort(numberComparator); + return offsets; + } + public nextConsecutivePositions(source: AbstractText, count: number): Position[] { const t = new PositionOffsetTransformer(source.getValue()); - const offsets = OffsetRange.ofLength(count).map(() => this.nextIntRange(0, t.text.length)); - offsets.sort(numberComparator); + const offsets = this.nextConsecutiveOffsets(new OffsetRange(0, t.text.length), count); return offsets.map(offset => t.getPosition(offset)); } @@ -58,12 +77,53 @@ export abstract class Random { for (let i = 0; i < singleTextEditCount; i++) { const start = positions[i * 2]; const end = positions[i * 2 + 1]; - const newText = this.nextString(end.column - start.column, Random.basicAlphabetMultiline); + const newText = this.nextString(end.column - start.column, this.stringGenerator(Random.basicAlphabetMultiline)); singleTextEdits.push(new SingleTextEdit(Range.fromPositions(start, end), newText)); } return new TextEdit(singleTextEdits).normalize(); } + + public nextOffsetEdit(target: string, singleTextEditCount: number, newTextAlphabet = Random.basicAlphabetMultiline): OffsetEdit { + const singleTextEdits: SingleOffsetEdit[] = []; + + const positions = this.nextConsecutiveOffsets(new OffsetRange(0, target.length), singleTextEditCount * 2); + + for (let i = 0; i < singleTextEditCount; i++) { + const start = positions[i * 2]; + const end = positions[i * 2 + 1]; + const range = new OffsetRange(start, end); + + const newTextLen = this.nextIntRange(range.isEmpty ? 1 : 0, 10); + const newText = this.nextString(newTextLen, this.stringGenerator(newTextAlphabet)); + singleTextEdits.push(new SingleOffsetEdit(range, newText)); + } + + return new OffsetEdit(singleTextEdits).normalize(); + } + + public nextSingleOffsetEdit(target: string, newTextAlphabet = Random.basicAlphabetMultiline): SingleOffsetEdit { + const edit = this.nextOffsetEdit(target, 1, newTextAlphabet); + return edit.edits[0]; + } +} + +export function sequenceGenerator(sequence: T[]): IGenerator { + let index = 0; + return { + next: () => { + if (index >= sequence.length) { + throw new BugIndicatingError('End of sequence'); + } + const element = sequence[index]; + index++; + return element; + } + }; +} + +export interface IGenerator { + next(): T; } class MersenneTwister extends Random { From ae7a7b1918d836b710d4f45f2d83c1a0d9994a1c Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 7 May 2025 10:38:46 +0000 Subject: [PATCH 68/90] =?UTF-8?q?SCM=20-=20=F0=9F=92=84=20refactor=20quick?= =?UTF-8?q?=20diffs=20so=20that=20they=20do=20not=20use=20the=20quick=20di?= =?UTF-8?q?ff=20label=20as=20a=20key=20(#248293)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SCM - cleanup label property * Fix navigating changes so that it factors in quick diff visibility --- .../contrib/scm/browser/quickDiffModel.ts | 29 +++++----- .../contrib/scm/browser/quickDiffWidget.ts | 53 ++++++++++--------- .../workbench/contrib/scm/common/quickDiff.ts | 1 - 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index ebdfee28049..240355a2301 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -290,7 +290,6 @@ export class QuickDiffModel extends Disposable { allDiffs.push({ providerId: quickDiff.id, - label: quickDiff.label, original: quickDiff.originalResource, modified: this._model.resource, change: diff.changes[index], @@ -303,11 +302,11 @@ export class QuickDiffModel extends Disposable { const sorted = allDiffs.sort((a, b) => compareChanges(a.change, b.change)); const map: Map = new Map(); for (let i = 0; i < sorted.length; i++) { - const label = sorted[i].label; - if (!map.has(label)) { - map.set(label, []); + const providerId = sorted[i].providerId; + if (!map.has(providerId)) { + map.set(providerId, []); } - map.get(label)!.push(i); + map.get(providerId)!.push(i); } return { changes: sorted, mapChanges: map }; }); @@ -401,16 +400,16 @@ export class QuickDiffModel extends Disposable { return this.quickDiffService.getQuickDiffs(uri, this._model.getLanguageId(), isSynchronized); } - findNextClosestChange(lineNumber: number, inclusive = true, provider?: string): number { - const visibleQuickDiffLabels = this.quickDiffs - .filter(quickDiff => (!provider || quickDiff.label === provider) && + findNextClosestChange(lineNumber: number, inclusive = true, providerId?: string): number { + const visibleQuickDiffIds = this.quickDiffs + .filter(quickDiff => (!providerId || quickDiff.id === providerId) && this.quickDiffService.isQuickDiffProviderVisible(quickDiff.id)) - .map(quickDiff => quickDiff.label); + .map(quickDiff => quickDiff.id); if (!inclusive) { // Next visible change let nextChangeIndex = this.changes - .findIndex(change => visibleQuickDiffLabels.includes(change.label) && + .findIndex(change => visibleQuickDiffIds.includes(change.providerId) && change.change.modifiedStartLineNumber > lineNumber); if (nextChangeIndex !== -1) { @@ -419,7 +418,7 @@ export class QuickDiffModel extends Disposable { // First visible change nextChangeIndex = this.changes - .findIndex(change => visibleQuickDiffLabels.includes(change.label)); + .findIndex(change => visibleQuickDiffIds.includes(change.providerId)); return nextChangeIndex !== -1 ? nextChangeIndex : 0; } @@ -438,7 +437,7 @@ export class QuickDiffModel extends Disposable { // Next visible change let nextChangeIndex = this.changes - .findIndex(change => visibleQuickDiffLabels.includes(change.label) && + .findIndex(change => visibleQuickDiffIds.includes(change.providerId) && change.change.modifiedStartLineNumber <= lineNumber && getModifiedEndLineNumber(change.change) >= lineNumber); @@ -448,14 +447,14 @@ export class QuickDiffModel extends Disposable { // First visible change nextChangeIndex = this.changes - .findIndex(change => visibleQuickDiffLabels.includes(change.label)); + .findIndex(change => visibleQuickDiffIds.includes(change.providerId)); return nextChangeIndex !== -1 ? nextChangeIndex : 0; } - findPreviousClosestChange(lineNumber: number, inclusive = true, provider?: string): number { + findPreviousClosestChange(lineNumber: number, inclusive = true, providerId?: string): number { for (let i = this.changes.length - 1; i >= 0; i--) { - if (provider && this.changes[i].label !== provider) { + if (providerId && this.changes[i].providerId !== providerId) { continue; } diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index 646f00f6ece..0ed0b86f0fd 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -54,7 +54,7 @@ import { quickDiffDecorationCount } from './quickDiffDecorator.js'; export const isQuickDiffVisible = new RawContextKey('dirtyDiffVisible', false); export interface IQuickDiffSelectItem extends ISelectOptionItem { - provider: string; + providerId: string; } export class QuickDiffPickerViewItem extends SelectActionViewItem { @@ -74,9 +74,9 @@ export class QuickDiffPickerViewItem extends SelectActionViewItem ({ provider, text: provider })); - const index = this.optionsItems.findIndex(item => item.provider === provider); + public setSelection(quickDiffs: QuickDiff[], providerId: string) { + this.optionsItems = quickDiffs.map(quickDiff => ({ providerId: quickDiff.id, text: quickDiff.label })); + const index = this.optionsItems.findIndex(item => item.providerId === providerId); this.setOptions(this.optionsItems, index); } @@ -149,7 +149,7 @@ class QuickDiffWidget extends PeekViewWidget { private title: string; private menu: IMenu | undefined; private _index: number = 0; - private _provider: string = ''; + private _providerId: string = ''; private change: IChange | undefined; private height: number | undefined = undefined; private dropdown: QuickDiffPickerViewItem | undefined; @@ -184,8 +184,8 @@ class QuickDiffWidget extends PeekViewWidget { this.setTitle(this.title); } - get provider(): string { - return this._provider; + get providerId(): string { + return this._providerId; } get index(): number { @@ -205,8 +205,8 @@ class QuickDiffWidget extends PeekViewWidget { this.contextKeyService.createKey('originalResourceScheme', this.model.changes[index].original.scheme); this.updateActions(); - this._provider = labeledChange.label; this.change = change; + this._providerId = labeledChange.providerId; if (Iterable.isEmpty(this.model.originalTextModels)) { return; @@ -231,8 +231,8 @@ class QuickDiffWidget extends PeekViewWidget { const editorHeightInLines = Math.floor(editorHeight / lineHeight); const height = Math.min(getChangeHeight(change) + /* padding */ 8, Math.floor(editorHeightInLines / 3)); - this.updateDropdown(labeledChange.label); - this.renderTitle(labeledChange.label); + this.renderTitle(); + this.updateDropdown(); const changeType = getChangeType(change); const changeTypeColor = getChangeTypeColor(this.themeService.getColorTheme(), changeType); @@ -241,7 +241,7 @@ class QuickDiffWidget extends PeekViewWidget { const providerSpecificChanges: IChange[] = []; let contextIndex = index; for (const change of this.model.changes) { - if (change.label === this.model.changes[this._index].label) { + if (change.providerId === this.model.changes[this._index].providerId) { providerSpecificChanges.push(change.change); if (labeledChange === change) { contextIndex = providerSpecificChanges.length - 1; @@ -256,12 +256,15 @@ class QuickDiffWidget extends PeekViewWidget { } } - private renderTitle(label: string): void { - const providerChanges = this.model.quickDiffChanges.get(label)!; + private renderTitle(): void { + const providerChanges = this.model.quickDiffChanges.get(this._providerId)!; const providerIndex = providerChanges.indexOf(this._index); let detail: string; if (!this.shouldUseDropdown()) { + const label = this.model.quickDiffs + .find(quickDiff => quickDiff.id === this._providerId)?.label ?? ''; + detail = this.model.changes.length > 1 ? nls.localize('changes', "{0} - {1} of {2} changes", label, providerIndex + 1, providerChanges.length) : nls.localize('change', "{0} - {1} of {2} change", label, providerIndex + 1, providerChanges.length); @@ -277,20 +280,20 @@ class QuickDiffWidget extends PeekViewWidget { } private switchQuickDiff(event?: IQuickDiffSelectItem) { - const newProvider = event?.provider; - if (newProvider === this.model.changes[this._index].label) { + const newProviderId = event?.providerId; + if (newProviderId === this.model.changes[this._index].providerId) { return; } let closestGreaterIndex = this._index < this.model.changes.length - 1 ? this._index + 1 : 0; for (let i = closestGreaterIndex; i !== this._index; i < this.model.changes.length - 1 ? i++ : i = 0) { - if (this.model.changes[i].label === newProvider) { + if (this.model.changes[i].providerId === newProviderId) { closestGreaterIndex = i; break; } } let closestLesserIndex = this._index > 0 ? this._index - 1 : this.model.changes.length - 1; for (let i = closestLesserIndex; i !== this._index; i > 0 ? i-- : i = this.model.changes.length - 1) { - if (this.model.changes[i].label === newProvider) { + if (this.model.changes[i].providerId === newProviderId) { closestLesserIndex = i; break; } @@ -327,9 +330,9 @@ class QuickDiffWidget extends PeekViewWidget { this._actionbarWidget.push(this._disposables.add(new Action('peekview.close', nls.localize('label.close', "Close"), ThemeIcon.asClassName(Codicon.close), true, () => this.dispose())), { label: false, icon: true }); } - private updateDropdown(label: string): void { + private updateDropdown(): void { const quickDiffs = this.getQuickDiffsContainingChange(); - this.dropdown?.setSelection(quickDiffs.map(quickDiff => quickDiff.label), label); + this.dropdown?.setSelection(quickDiffs, this._providerId); } private getQuickDiffsContainingChange(): QuickDiff[] { @@ -542,10 +545,10 @@ export class QuickDiffEditorController extends Disposable implements IEditorCont } let index: number; - if (this.editor.hasModel() && (typeof lineNumber === 'number' || !this.widget.provider)) { - index = this.model.findNextClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber, true, this.widget.provider); + if (this.editor.hasModel() && (typeof lineNumber === 'number' || !this.widget.providerId)) { + index = this.model.findNextClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber, true, this.widget.providerId); } else { - const providerChanges: number[] = this.model.quickDiffChanges.get(this.widget.provider) ?? this.model.quickDiffChanges.values().next().value!; + const providerChanges: number[] = this.model.quickDiffChanges.get(this.widget.providerId) ?? this.model.quickDiffChanges.values().next().value!; const mapIndex = providerChanges.findIndex(value => value === this.widget!.index); index = providerChanges[rot(mapIndex + 1, providerChanges.length)]; } @@ -562,10 +565,10 @@ export class QuickDiffEditorController extends Disposable implements IEditorCont } let index: number; - if (this.editor.hasModel() && (typeof lineNumber === 'number' || !this.widget.provider)) { - index = this.model.findPreviousClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber, true, this.widget.provider); + if (this.editor.hasModel() && (typeof lineNumber === 'number' || !this.widget.providerId)) { + index = this.model.findPreviousClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber, true, this.widget.providerId); } else { - const providerChanges: number[] = this.model.quickDiffChanges.get(this.widget.provider) ?? this.model.quickDiffChanges.values().next().value!; + const providerChanges: number[] = this.model.quickDiffChanges.get(this.widget.providerId) ?? this.model.quickDiffChanges.values().next().value!; const mapIndex = providerChanges.findIndex(value => value === this.widget!.index); index = providerChanges[rot(mapIndex - 1, providerChanges.length)]; } diff --git a/src/vs/workbench/contrib/scm/common/quickDiff.ts b/src/vs/workbench/contrib/scm/common/quickDiff.ts index e1c0b0bf437..bc994f648cb 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiff.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiff.ts @@ -84,7 +84,6 @@ export interface QuickDiff { export interface QuickDiffChange { readonly providerId: string; - readonly label: string; readonly original: URI; readonly modified: URI; readonly change: IChange; From 20ec16985de4763b3af6d01911ed783c2fe32c85 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 12:22:21 +0200 Subject: [PATCH 69/90] Moves rarely used/very specific utilities into editor/common/core/misc --- src/vs/editor/browser/viewParts/minimap/minimap.ts | 2 +- .../editor/browser/viewParts/minimap/minimapCharRenderer.ts | 2 +- src/vs/editor/common/config/editorConfigurationSchema.ts | 2 +- src/vs/editor/common/config/editorOptions.ts | 2 +- src/vs/editor/common/core/{ => misc}/eolCounter.ts | 2 +- src/vs/editor/common/core/{ => misc}/indentation.ts | 4 ++-- src/vs/editor/common/core/{ => misc}/rgba.ts | 0 src/vs/editor/common/core/{ => misc}/textModelDefaults.ts | 0 src/vs/editor/common/cursorCommon.ts | 2 +- .../common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts | 2 +- src/vs/editor/common/model/textModel.ts | 6 +++--- src/vs/editor/common/model/textModelTokens.ts | 2 +- src/vs/editor/common/model/tokenizationTextModelPart.ts | 2 +- src/vs/editor/common/services/modelService.ts | 2 +- src/vs/editor/common/tokens/contiguousMultilineTokens.ts | 2 +- src/vs/editor/common/tokens/sparseMultilineTokens.ts | 2 +- src/vs/editor/common/viewModel/minimapTokensColorTracker.ts | 2 +- src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts | 2 +- src/vs/editor/contrib/indentation/common/indentation.ts | 2 +- src/vs/editor/test/browser/view/minimapCharRenderer.test.ts | 2 +- .../textMateWorkerTokenizerController.ts | 2 +- 21 files changed, 22 insertions(+), 22 deletions(-) rename src/vs/editor/common/core/{ => misc}/eolCounter.ts (95%) rename src/vs/editor/common/core/{ => misc}/indentation.ts (92%) rename src/vs/editor/common/core/{ => misc}/rgba.ts (100%) rename src/vs/editor/common/core/{ => misc}/textModelDefaults.ts (100%) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index d57f8a9a28c..63919280735 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -15,7 +15,7 @@ import { ILine, RenderedLinesCollection } from '../../view/viewLayer.js'; import { PartFingerprint, PartFingerprints, ViewPart } from '../../view/viewPart.js'; import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH, EditorLayoutInfoComputer } from '../../../common/config/editorOptions.js'; import { Range } from '../../../common/core/range.js'; -import { RGBA8 } from '../../../common/core/rgba.js'; +import { RGBA8 } from '../../../common/core/misc/rgba.js'; import { ScrollType } from '../../../common/editorCommon.js'; import { IEditorConfiguration } from '../../../common/config/editorConfiguration.js'; import { ColorId } from '../../../common/encodedTokenAttributes.js'; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts index 687ab96b521..cb860e767cb 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RGBA8 } from '../../../common/core/rgba.js'; +import { RGBA8 } from '../../../common/core/misc/rgba.js'; import { Constants, getCharIndex } from './minimapCharSheet.js'; import { toUint8 } from '../../../../base/common/uint.js'; diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 8b8d099149a..bc8b7e19f2f 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -6,7 +6,7 @@ import type { IJSONSchemaSnippet } from '../../../base/common/jsonSchema.js'; import { diffEditorDefaultOptions } from './diffEditor.js'; import { editorOptionsRegistry } from './editorOptions.js'; -import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; +import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js'; import * as nls from '../../../nls.js'; import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationPropertySchema, IConfigurationRegistry } from '../../../platform/configuration/common/configurationRegistry.js'; import { Registry } from '../../../platform/registry/common/platform.js'; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index bb4eee89030..4beb40d2156 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -11,7 +11,7 @@ import * as platform from '../../../base/common/platform.js'; import { ScrollbarVisibility } from '../../../base/common/scrollable.js'; import { Constants } from '../../../base/common/uint.js'; import { FontInfo } from './fontInfo.js'; -import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; +import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js'; import { USUAL_WORD_SEPARATORS } from '../core/wordHelper.js'; import * as nls from '../../../nls.js'; import { AccessibilitySupport } from '../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/editor/common/core/eolCounter.ts b/src/vs/editor/common/core/misc/eolCounter.ts similarity index 95% rename from src/vs/editor/common/core/eolCounter.ts rename to src/vs/editor/common/core/misc/eolCounter.ts index 42175b1a3f8..f37989cb442 100644 --- a/src/vs/editor/common/core/eolCounter.ts +++ b/src/vs/editor/common/core/misc/eolCounter.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; +import { CharCode } from '../../../../base/common/charCode.js'; export const enum StringEOL { Unknown = 0, diff --git a/src/vs/editor/common/core/indentation.ts b/src/vs/editor/common/core/misc/indentation.ts similarity index 92% rename from src/vs/editor/common/core/indentation.ts rename to src/vs/editor/common/core/misc/indentation.ts index 03ce99442c8..5e6462f8649 100644 --- a/src/vs/editor/common/core/indentation.ts +++ b/src/vs/editor/common/core/misc/indentation.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from '../../../base/common/strings.js'; -import { CursorColumns } from './cursorColumns.js'; +import * as strings from '../../../../base/common/strings.js'; +import { CursorColumns } from '../cursorColumns.js'; function _normalizeIndentationFromWhitespace(str: string, indentSize: number, insertSpaces: boolean): string { let spacesCnt = 0; diff --git a/src/vs/editor/common/core/rgba.ts b/src/vs/editor/common/core/misc/rgba.ts similarity index 100% rename from src/vs/editor/common/core/rgba.ts rename to src/vs/editor/common/core/misc/rgba.ts diff --git a/src/vs/editor/common/core/textModelDefaults.ts b/src/vs/editor/common/core/misc/textModelDefaults.ts similarity index 100% rename from src/vs/editor/common/core/textModelDefaults.ts rename to src/vs/editor/common/core/misc/textModelDefaults.ts diff --git a/src/vs/editor/common/cursorCommon.ts b/src/vs/editor/common/cursorCommon.ts index 2e1c78484fc..9497e9d4651 100644 --- a/src/vs/editor/common/cursorCommon.ts +++ b/src/vs/editor/common/cursorCommon.ts @@ -16,7 +16,7 @@ import { ILanguageConfigurationService } from './languages/languageConfiguration import { createScopedLineTokens } from './languages/supports.js'; import { IElectricAction } from './languages/supports/electricCharacter.js'; import { CursorColumns } from './core/cursorColumns.js'; -import { normalizeIndentation } from './core/indentation.js'; +import { normalizeIndentation } from './core/misc/indentation.js'; import { InputMode } from './inputMode.js'; export interface IColumnSelectData { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 42d1995e367..078b190bb7b 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -9,7 +9,7 @@ import { Position } from '../../core/position.js'; import { Range } from '../../core/range.js'; import { ApplyEditsResult, EndOfLinePreference, FindMatch, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer, ITextSnapshot, ValidAnnotatedEditOperation, IValidEditOperation, SearchData } from '../../model.js'; import { PieceTreeBase, StringBuffer } from './pieceTreeBase.js'; -import { countEOL, StringEOL } from '../../core/eolCounter.js'; +import { countEOL, StringEOL } from '../../core/misc/eolCounter.js'; import { TextChange } from '../../core/textChange.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 0ea09949e81..b702b86937e 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -16,13 +16,13 @@ import { ThemeColor } from '../../../base/common/themables.js'; import { Constants } from '../../../base/common/uint.js'; import { URI } from '../../../base/common/uri.js'; import { ISingleEditOperation } from '../core/editOperation.js'; -import { countEOL } from '../core/eolCounter.js'; -import { normalizeIndentation } from '../core/indentation.js'; +import { countEOL } from '../core/misc/eolCounter.js'; +import { normalizeIndentation } from '../core/misc/indentation.js'; import { IPosition, Position } from '../core/position.js'; import { IRange, Range } from '../core/range.js'; import { Selection } from '../core/selection.js'; import { TextChange } from '../core/textChange.js'; -import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; +import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js'; import { IWordAtPosition } from '../core/wordHelper.js'; import { FormattingOptions } from '../languages.js'; import { ILanguageSelection, ILanguageService } from '../languages/language.js'; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index b3253f37b4d..3a748495b9b 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -7,7 +7,7 @@ import { IdleDeadline, runWhenGlobalIdle } from '../../../base/common/async.js'; import { BugIndicatingError, onUnexpectedError } from '../../../base/common/errors.js'; import { setTimeout0 } from '../../../base/common/platform.js'; import { StopWatch } from '../../../base/common/stopwatch.js'; -import { countEOL } from '../core/eolCounter.js'; +import { countEOL } from '../core/misc/eolCounter.js'; import { LineRange } from '../core/lineRange.js'; import { OffsetRange } from '../core/offsetRange.js'; import { Position } from '../core/position.js'; diff --git a/src/vs/editor/common/model/tokenizationTextModelPart.ts b/src/vs/editor/common/model/tokenizationTextModelPart.ts index 8b1ea211b93..f0bc53336ac 100644 --- a/src/vs/editor/common/model/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/model/tokenizationTextModelPart.ts @@ -7,7 +7,7 @@ import { CharCode } from '../../../base/common/charCode.js'; import { BugIndicatingError, onUnexpectedError } from '../../../base/common/errors.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { DisposableMap, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js'; -import { countEOL } from '../core/eolCounter.js'; +import { countEOL } from '../core/misc/eolCounter.js'; import { LineRange } from '../core/lineRange.js'; import { IPosition, Position } from '../core/position.js'; import { Range } from '../core/range.js'; diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/services/modelService.ts index c0293db8390..eefda2797b9 100644 --- a/src/vs/editor/common/services/modelService.ts +++ b/src/vs/editor/common/services/modelService.ts @@ -11,7 +11,7 @@ import { EditOperation, ISingleEditOperation } from '../core/editOperation.js'; import { Range } from '../core/range.js'; import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from '../model.js'; import { TextModel, createTextBuffer } from '../model/textModel.js'; -import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; +import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js'; import { IModelLanguageChangedEvent } from '../textModelEvents.js'; import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; import { ILanguageSelection } from '../languages/language.js'; diff --git a/src/vs/editor/common/tokens/contiguousMultilineTokens.ts b/src/vs/editor/common/tokens/contiguousMultilineTokens.ts index 3e3e8886f1b..5a360be1c7c 100644 --- a/src/vs/editor/common/tokens/contiguousMultilineTokens.ts +++ b/src/vs/editor/common/tokens/contiguousMultilineTokens.ts @@ -7,7 +7,7 @@ import * as arrays from '../../../base/common/arrays.js'; import { readUInt32BE, writeUInt32BE } from '../../../base/common/buffer.js'; import { Position } from '../core/position.js'; import { IRange } from '../core/range.js'; -import { countEOL } from '../core/eolCounter.js'; +import { countEOL } from '../core/misc/eolCounter.js'; import { ContiguousTokensEditing } from './contiguousTokensEditing.js'; import { LineRange } from '../core/lineRange.js'; diff --git a/src/vs/editor/common/tokens/sparseMultilineTokens.ts b/src/vs/editor/common/tokens/sparseMultilineTokens.ts index 931e55eb3cc..706f8b2fb0a 100644 --- a/src/vs/editor/common/tokens/sparseMultilineTokens.ts +++ b/src/vs/editor/common/tokens/sparseMultilineTokens.ts @@ -6,7 +6,7 @@ import { CharCode } from '../../../base/common/charCode.js'; import { Position } from '../core/position.js'; import { IRange, Range } from '../core/range.js'; -import { countEOL } from '../core/eolCounter.js'; +import { countEOL } from '../core/misc/eolCounter.js'; import { ITextModel } from '../model.js'; import { RateLimiter } from './common.js'; diff --git a/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts b/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts index a0f36078c5b..7fe9093e983 100644 --- a/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts +++ b/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, markAsSingleton } from '../../../base/common/lifecycle.js'; -import { RGBA8 } from '../core/rgba.js'; +import { RGBA8 } from '../core/misc/rgba.js'; import { TokenizationRegistry } from '../languages.js'; import { ColorId } from '../encodedTokenAttributes.js'; diff --git a/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts b/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts index 9994afcb847..f7927e0877b 100644 --- a/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts +++ b/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts @@ -10,7 +10,7 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import { IRange, Range } from '../../../common/core/range.js'; import { Selection } from '../../../common/core/selection.js'; import { IModelContentChangedEvent } from '../../../common/textModelEvents.js'; -import { countEOL } from '../../../common/core/eolCounter.js'; +import { countEOL } from '../../../common/core/misc/eolCounter.js'; import { FoldingModel } from './foldingModel.js'; export class HiddenRangeModel { diff --git a/src/vs/editor/contrib/indentation/common/indentation.ts b/src/vs/editor/contrib/indentation/common/indentation.ts index f1046f84728..9daefb1d84b 100644 --- a/src/vs/editor/contrib/indentation/common/indentation.ts +++ b/src/vs/editor/contrib/indentation/common/indentation.ts @@ -6,7 +6,7 @@ import * as strings from '../../../../base/common/strings.js'; import { ShiftCommand } from '../../../common/commands/shiftCommand.js'; import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { normalizeIndentation } from '../../../common/core/indentation.js'; +import { normalizeIndentation } from '../../../common/core/misc/indentation.js'; import { Selection } from '../../../common/core/selection.js'; import { StandardTokenType } from '../../../common/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; diff --git a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts index 20af8f3d03f..36dd0e9f6bc 100644 --- a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { MinimapCharRendererFactory } from '../../../browser/viewParts/minimap/minimapCharRendererFactory.js'; import { Constants } from '../../../browser/viewParts/minimap/minimapCharSheet.js'; -import { RGBA8 } from '../../../common/core/rgba.js'; +import { RGBA8 } from '../../../common/core/misc/rgba.js'; suite('MinimapCharRenderer', () => { diff --git a/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts b/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts index b42fdfdaf3d..e0d5a78cca2 100644 --- a/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts +++ b/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts @@ -7,7 +7,7 @@ import { importAMDNodeModule } from '../../../../../amdX.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { IObservable, autorun, keepObserved } from '../../../../../base/common/observable.js'; import { Proxied } from '../../../../../base/common/worker/webWorker.js'; -import { countEOL } from '../../../../../editor/common/core/eolCounter.js'; +import { countEOL } from '../../../../../editor/common/core/misc/eolCounter.js'; import { LineRange } from '../../../../../editor/common/core/lineRange.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { IBackgroundTokenizationStore, ILanguageIdCodec } from '../../../../../editor/common/languages.js'; From f9d15472a084878639bbb27a36ba05487ecee317 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 12:05:04 +0200 Subject: [PATCH 70/90] Moves data structures into vs/editor/common/core/2d --- src/vs/editor/browser/config/editorConfiguration.ts | 2 +- src/vs/editor/browser/config/elementSizeObserver.ts | 2 +- src/vs/editor/browser/editorBrowser.ts | 2 +- src/vs/editor/browser/observableCodeEditor.ts | 2 +- .../editor/browser/viewParts/contentWidgets/contentWidgets.ts | 2 +- src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts | 2 +- .../editor/browser/widget/diffEditor/delegatingEditorImpl.ts | 2 +- src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts | 2 +- src/vs/editor/common/config/editorConfiguration.ts | 2 +- src/vs/editor/common/core/{ => 2d}/dimension.ts | 0 src/vs/editor/{browser => common/core/2d}/point.ts | 3 +-- src/vs/editor/{browser => common/core/2d}/rect.ts | 4 ++-- src/vs/editor/common/editorCommon.ts | 2 +- .../view/inlineEdits/components/gutterIndicatorView.ts | 4 ++-- .../view/inlineEdits/inlineEditsViews/debugVisualization.ts | 2 +- .../inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts | 2 +- .../inlineEdits/inlineEditsViews/inlineEditsCustomView.ts | 2 +- .../inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts | 2 +- .../inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts | 2 +- .../inlineEditsViews/inlineEditsLineReplacementView.ts | 4 ++-- .../inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts | 2 +- .../inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts | 4 ++-- .../inlineEditsViews/inlineEditsWordReplacementView.ts | 4 ++-- .../inlineCompletions/browser/view/inlineEdits/utils/utils.ts | 4 ++-- src/vs/editor/contrib/rename/browser/renameWidget.ts | 2 +- src/vs/workbench/contrib/chat/browser/chatInputPart.ts | 2 +- src/vs/workbench/contrib/debug/browser/debugHover.ts | 2 +- .../contrib/notebook/browser/notebookEditorWidget.ts | 2 +- .../contrib/notebook/browser/view/cellParts/codeCell.ts | 2 +- .../workbench/services/editor/common/editorGroupsService.ts | 2 +- 30 files changed, 35 insertions(+), 36 deletions(-) rename src/vs/editor/common/core/{ => 2d}/dimension.ts (100%) rename src/vs/editor/{browser => common/core/2d}/point.ts (96%) rename src/vs/editor/{browser => common/core/2d}/rect.ts (98%) diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts index 1bf70f155d7..5ed596229cc 100644 --- a/src/vs/editor/browser/config/editorConfiguration.ts +++ b/src/vs/editor/browser/config/editorConfiguration.ts @@ -16,7 +16,7 @@ import { TabFocus } from './tabFocus.js'; import { ComputeOptionsMemory, ConfigurationChangedEvent, EditorOption, editorOptionsRegistry, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, IEnvironmentalOptions } from '../../common/config/editorOptions.js'; import { EditorZoom } from '../../common/config/editorZoom.js'; import { BareFontInfo, FontInfo, IValidatedEditorOptions } from '../../common/config/fontInfo.js'; -import { IDimension } from '../../common/core/dimension.js'; +import { IDimension } from '../../common/core/2d/dimension.js'; import { IEditorConfiguration } from '../../common/config/editorConfiguration.js'; import { AccessibilitySupport, IAccessibilityService } from '../../../platform/accessibility/common/accessibility.js'; import { getWindow, getWindowById } from '../../../base/browser/dom.js'; diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index 303d6377cdf..f6c6ac5e926 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../base/common/lifecycle.js'; -import { IDimension } from '../../common/core/dimension.js'; +import { IDimension } from '../../common/core/2d/dimension.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { getWindow, scheduleAtNextAnimationFrame } from '../../../base/browser/dom.js'; diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 8f0ed8dabb8..69b6021983e 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -9,7 +9,7 @@ import { IBoundarySashes } from '../../base/browser/ui/sash/sash.js'; import { Event } from '../../base/common/event.js'; import { IEditorConstructionOptions } from './config/editorConfiguration.js'; import { ConfigurationChangedEvent, EditorLayoutInfo, EditorOption, FindComputedEditorOptionValueById, IComputedEditorOptions, IDiffEditorOptions, IEditorOptions, OverviewRulerPosition } from '../common/config/editorOptions.js'; -import { IDimension } from '../common/core/dimension.js'; +import { IDimension } from '../common/core/2d/dimension.js'; import { IPosition, Position } from '../common/core/position.js'; import { IRange, Range } from '../common/core/range.js'; import { Selection } from '../common/core/selection.js'; diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index eb5172693ce..bfdbb5b8238 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -15,7 +15,7 @@ import { ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; import { IModelDeltaDecoration, ITextModel } from '../common/model.js'; import { IModelContentChangedEvent } from '../common/textModelEvents.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, IOverlayWidget, IOverlayWidgetPosition, IPasteEvent } from './editorBrowser.js'; -import { Point } from './point.js'; +import { Point } from '../common/core/2d/point.js'; /** * Returns a facade for the code editor that provides observables for various states/events. diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index f3e89362b99..974c98bbc3d 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -12,7 +12,7 @@ import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IDimension } from '../../../common/core/dimension.js'; +import { IDimension } from '../../../common/core/2d/dimension.js'; import { PositionAffinity } from '../../../common/model.js'; import { IPosition, Position } from '../../../common/core/position.js'; import { IViewModel } from '../../../common/viewModel.js'; diff --git a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts index f2a45afad39..0ea325c3b1b 100644 --- a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts @@ -28,7 +28,7 @@ import { CodeEditorContributions } from './codeEditorContributions.js'; import { IEditorConfiguration } from '../../../common/config/editorConfiguration.js'; import { ConfigurationChangedEvent, EditorLayoutInfo, EditorOption, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, filterValidationDecorations } from '../../../common/config/editorOptions.js'; import { CursorColumns } from '../../../common/core/cursorColumns.js'; -import { IDimension } from '../../../common/core/dimension.js'; +import { IDimension } from '../../../common/core/2d/dimension.js'; import { editorUnnecessaryCodeOpacity } from '../../../common/core/editorColorRegistry.js'; import { IPosition, Position } from '../../../common/core/position.js'; import { IRange, Range } from '../../../common/core/range.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts b/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts index 056db97f620..0152dc4eb0f 100644 --- a/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts +++ b/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts @@ -7,7 +7,7 @@ import { Emitter } from '../../../../base/common/event.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { CodeEditorWidget } from '../codeEditor/codeEditorWidget.js'; import { IEditorOptions } from '../../../common/config/editorOptions.js'; -import { IDimension } from '../../../common/core/dimension.js'; +import { IDimension } from '../../../common/core/2d/dimension.js'; import { IPosition, Position } from '../../../common/core/position.js'; import { IRange, Range } from '../../../common/core/range.js'; import { ISelection, Selection } from '../../../common/core/selection.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 046f825dfe8..eb130dd134b 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -17,7 +17,7 @@ import { ServiceCollection } from '../../../../platform/instantiation/common/ser import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; import { IEditorProgressService } from '../../../../platform/progress/common/progress.js'; import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; -import { IDimension } from '../../../common/core/dimension.js'; +import { IDimension } from '../../../common/core/2d/dimension.js'; import { Position } from '../../../common/core/position.js'; import { Range } from '../../../common/core/range.js'; import { CursorChangeReason, ICursorPositionChangedEvent } from '../../../common/cursorEvents.js'; diff --git a/src/vs/editor/common/config/editorConfiguration.ts b/src/vs/editor/common/config/editorConfiguration.ts index 08f45386aa2..494440bc5aa 100644 --- a/src/vs/editor/common/config/editorConfiguration.ts +++ b/src/vs/editor/common/config/editorConfiguration.ts @@ -6,7 +6,7 @@ import { Event } from '../../../base/common/event.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; import { ConfigurationChangedEvent, IComputedEditorOptions, IEditorOptions } from './editorOptions.js'; -import { IDimension } from '../core/dimension.js'; +import { IDimension } from '../core/2d/dimension.js'; import { MenuId } from '../../../platform/actions/common/actions.js'; export interface IEditorConfiguration extends IDisposable { diff --git a/src/vs/editor/common/core/dimension.ts b/src/vs/editor/common/core/2d/dimension.ts similarity index 100% rename from src/vs/editor/common/core/dimension.ts rename to src/vs/editor/common/core/2d/dimension.ts diff --git a/src/vs/editor/browser/point.ts b/src/vs/editor/common/core/2d/point.ts similarity index 96% rename from src/vs/editor/browser/point.ts rename to src/vs/editor/common/core/2d/point.ts index 8a41c329f18..586367df62c 100644 --- a/src/vs/editor/browser/point.ts +++ b/src/vs/editor/common/core/2d/point.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ export class Point { - static equals(a: Point, b: Point): boolean { return a.x === b.x && a.y === b.y; } constructor( public readonly x: number, - public readonly y: number + public readonly y: number, ) { } public add(other: Point): Point { diff --git a/src/vs/editor/browser/rect.ts b/src/vs/editor/common/core/2d/rect.ts similarity index 98% rename from src/vs/editor/browser/rect.ts rename to src/vs/editor/common/core/2d/rect.ts index e6de59e4fc8..edafc15854a 100644 --- a/src/vs/editor/browser/rect.ts +++ b/src/vs/editor/common/core/2d/rect.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../base/common/errors.js'; -import { OffsetRange } from '../common/core/offsetRange.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetRange } from '../offsetRange.js'; import { Point } from './point.js'; export class Rect { diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 9cf2cd59a83..3d0210dc4c4 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -9,7 +9,7 @@ import { IDisposable } from '../../base/common/lifecycle.js'; import { ThemeColor } from '../../base/common/themables.js'; import { URI, UriComponents } from '../../base/common/uri.js'; import { IEditorOptions } from './config/editorOptions.js'; -import { IDimension } from './core/dimension.js'; +import { IDimension } from './core/2d/dimension.js'; import { IPosition, Position } from './core/position.js'; import { IRange, Range } from './core/range.js'; import { ISelection, Selection } from './core/selection.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts index fc90fe16e58..0ce8e0c18d3 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts @@ -16,8 +16,8 @@ import { asCssVariable } from '../../../../../../../platform/theme/common/colorU import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; import { IEditorMouseEvent } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Point } from '../../../../../../common/core/2d/point.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { HoverService } from '../../../../../../browser/services/hoverService/hoverService.js'; import { HoverWidget } from '../../../../../../browser/services/hoverService/hoverWidget.js'; import { EditorOption, RenderLineNumbersType } from '../../../../../../common/config/editorOptions.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/debugVisualization.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/debugVisualization.ts index 43af6390f82..a316a9a1971 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/debugVisualization.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/debugVisualization.ts @@ -5,7 +5,7 @@ import { IDisposable } from '../../../../../../../base/common/lifecycle.js'; import { IReader, derivedWithStore } from '../../../../../../../base/common/observable.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; export interface IVisualizationEffect { visualize(): IDisposable; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts index 458aacf7533..afddfc1acc6 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCollapsedView.ts @@ -11,7 +11,7 @@ import { IAccessibilityService } from '../../../../../../../platform/accessibili import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; +import { Point } from '../../../../../../common/core/2d/point.js'; import { singleTextRemoveCommonPrefix } from '../../../model/singleTextEditHelpers.js'; import { IInlineEditsView } from '../inlineEditsViewInterface.js'; import { InlineEditWithChanges } from '../inlineEditWithChanges.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts index 16fad4d39ac..09e36074a07 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts @@ -12,7 +12,7 @@ import { asCssVariable } from '../../../../../../../platform/theme/common/colorU import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts index 253d45b1ea5..89bf9d86871 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts @@ -11,7 +11,7 @@ import { editorBackground } from '../../../../../../../platform/theme/common/col import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts index 6cafdf6f116..8ebcb209830 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts @@ -12,7 +12,7 @@ import { editorBackground } from '../../../../../../../platform/theme/common/col import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts index 0d5e96cee9f..7df8e3af3bb 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts @@ -14,8 +14,8 @@ import { IThemeService } from '../../../../../../../platform/theme/common/themeS import { IEditorMouseEvent, IViewZoneChangeAccessor } from '../../../../../../browser/editorBrowser.js'; import { EditorMouseEvent } from '../../../../../../browser/editorDom.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Point } from '../../../../../../common/core/2d/point.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts index 9e30a4860dd..e06415d014c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts @@ -14,7 +14,7 @@ import { asCssVariable, asCssVariableWithDefault } from '../../../../../../../pl import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { EmbeddedCodeEditorWidget } from '../../../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts index 55ee6e9b87d..7616d71cfd7 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts @@ -10,8 +10,8 @@ import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { constObservable, derived, IObservable } from '../../../../../../../base/common/observable.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Point } from '../../../../../../common/core/2d/point.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { SingleTextEdit } from '../../../../../../common/core/textEdit.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts index 2702159e1d2..55339389d22 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts @@ -11,8 +11,8 @@ import { constObservable, derived, IObservable, observableValue } from '../../.. import { editorBackground, editorHoverForeground } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Point } from '../../../../../../common/core/2d/point.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { SingleOffsetEdit } from '../../../../../../common/core/offsetEdit.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts index 8aac0066ab6..e07a71dab93 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts @@ -15,8 +15,8 @@ import { URI } from '../../../../../../../base/common/uri.js'; import { MenuEntryActionViewItem } from '../../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; -import { Rect } from '../../../../../../browser/rect.js'; +import { Point } from '../../../../../../common/core/2d/point.js'; +import { Rect } from '../../../../../../common/core/2d/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; diff --git a/src/vs/editor/contrib/rename/browser/renameWidget.ts b/src/vs/editor/contrib/rename/browser/renameWidget.ts index e5a7bc71497..d2ce61a6b59 100644 --- a/src/vs/editor/contrib/rename/browser/renameWidget.ts +++ b/src/vs/editor/contrib/rename/browser/renameWidget.ts @@ -24,7 +24,7 @@ import * as domFontInfo from '../../../browser/config/domFontInfo.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { FontInfo } from '../../../common/config/fontInfo.js'; -import { IDimension } from '../../../common/core/dimension.js'; +import { IDimension } from '../../../common/core/2d/dimension.js'; import { Position } from '../../../common/core/position.js'; import { IRange, Range } from '../../../common/core/range.js'; import { ScrollType } from '../../../common/editorCommon.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index b06aef2e185..abd69a5bb1d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -28,7 +28,7 @@ import { IEditorConstructionOptions } from '../../../../editor/browser/config/ed import { EditorExtensionsRegistry } from '../../../../editor/browser/editorExtensions.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { EditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { IDimension } from '../../../../editor/common/core/dimension.js'; +import { IDimension } from '../../../../editor/common/core/2d/dimension.js'; import { IPosition } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; import { ITextModel } from '../../../../editor/common/model.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 2d2a3ea5f63..28f66c5d519 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -20,7 +20,7 @@ import { isMacintosh } from '../../../../base/common/platform.js'; import { ScrollbarVisibility } from '../../../../base/common/scrollable.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../editor/browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { IDimension } from '../../../../editor/common/core/dimension.js'; +import { IDimension } from '../../../../editor/common/core/2d/dimension.js'; import { Position } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index c6d652b63a0..2b6a76431fb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -91,7 +91,7 @@ import { IEditorGroupsService } from '../../../services/editor/common/editorGrou import { NotebookPerfMarks } from '../common/notebookPerformance.js'; import { BaseCellEditorOptions } from './viewModel/cellEditorOptions.js'; import { FloatingEditorClickMenu } from '../../../browser/codeeditor.js'; -import { IDimension } from '../../../../editor/common/core/dimension.js'; +import { IDimension } from '../../../../editor/common/core/2d/dimension.js'; import { CellFindMatchModel } from './contrib/find/findModel.js'; import { INotebookLoggingService } from '../common/notebookLoggingService.js'; import { Schemas } from '../../../../base/common/network.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index 5f1497f644f..645c7b9a704 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -14,7 +14,7 @@ import { clamp } from '../../../../../../base/common/numbers.js'; import * as strings from '../../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; -import { IDimension } from '../../../../../../editor/common/core/dimension.js'; +import { IDimension } from '../../../../../../editor/common/core/2d/dimension.js'; import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; import { tokenizeToStringSync } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; import { IReadonlyTextBuffer, ITextModel } from '../../../../../../editor/common/model.js'; diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 018c4348b9a..f1154ed8d47 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -9,7 +9,7 @@ import { IEditorPane, GroupIdentifier, EditorInputWithOptions, CloseDirection, I import { EditorInput } from '../../../common/editor/editorInput.js'; import { IEditorOptions } from '../../../../platform/editor/common/editor.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IDimension } from '../../../../editor/common/core/dimension.js'; +import { IDimension } from '../../../../editor/common/core/2d/dimension.js'; import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { ContextKeyValue, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { URI } from '../../../../base/common/uri.js'; From b2860b1398320d46b9609cd0ad6b73b6b099992f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 12:23:21 +0200 Subject: [PATCH 71/90] Fixes ci --- build/monaco/monaco.d.ts.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index cb4c9082a11..0a5f68b9a7c 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -114,7 +114,7 @@ export interface ICommandMetadata { #include(vs/editor/common/core/wordHelper): IWordAtPosition #includeAll(vs/editor/common/model): IScrollEvent #include(vs/editor/common/diff/legacyLinesDiffComputer): IChange, ICharChange, ILineChange -#include(vs/editor/common/core/dimension): IDimension +#include(vs/editor/common/core/2d/dimension): IDimension #includeAll(vs/editor/common/editorCommon): IScrollEvent #includeAll(vs/editor/common/textModelEvents): #includeAll(vs/editor/common/cursorEvents): From ca8d9863403beb1de98c5da12b8ffdce1560cd78 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 May 2025 12:44:06 +0200 Subject: [PATCH 72/90] process explorer - adopt custom hover (#248295) --- .../processExplorerControl.ts | 50 ++++++++++++++++--- .../processExplorerEditorInput.ts | 2 + 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts index a027b854676..a3fd6268701 100644 --- a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts @@ -27,6 +27,9 @@ import { RenderIndentGuides } from '../../../../base/browser/ui/tree/abstractTre import { isWindows } from '../../../../base/common/platform.js'; import { IProcessMainService } from '../../../../platform/process/common/process.js'; import { Delayer } from '../../../../base/common/async.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IManagedHover } from '../../../../base/browser/ui/hover/hover.js'; +import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; const DEBUG_FLAGS_PATTERN = /\s--inspect(?:-brk|port)?=(?\d+)?/; const DEBUG_PORT_PATTERN = /\s--inspect-port=(?\d+)/; @@ -153,6 +156,7 @@ interface IProcessItemTemplateData extends IProcessRowTemplateData { readonly cpu: HTMLElement; readonly memory: HTMLElement; readonly pid: HTMLElement; + readonly hover?: ProcessItemHover; } class ProcessHeaderTreeRenderer implements ITreeRenderer { @@ -213,14 +217,48 @@ class ErrorRenderer implements ITreeRenderer { readonly templateId: string = 'process'; - constructor(private totalMem: number, private model: ProcessExplorerModel) { } + constructor( + private totalMem: number, + private model: ProcessExplorerModel, + @IHoverService private readonly hoverService: IHoverService + ) { } renderTemplate(container: HTMLElement): IProcessItemTemplateData { - return createRow(container); + const row = createRow(container); + + return { + name: row.name, + cpu: row.cpu, + memory: row.memory, + pid: row.pid, + hover: new ProcessItemHover(row.name, this.hoverService) + }; } renderElement(node: ITreeNode, index: number, templateData: IProcessItemTemplateData, height: number | undefined): void { @@ -229,18 +267,18 @@ class ProcessRenderer implements ITreeRenderer Date: Wed, 7 May 2025 12:31:31 +0200 Subject: [PATCH 73/90] Makes _createNew protected. --- src/vs/editor/common/core/edits/arrayEdit.ts | 2 +- src/vs/editor/common/core/edits/edit.ts | 2 +- src/vs/editor/common/core/edits/lengthEdit.ts | 2 +- src/vs/editor/common/core/edits/stringEdit.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/core/edits/arrayEdit.ts b/src/vs/editor/common/core/edits/arrayEdit.ts index ab88e206792..f5905c2c164 100644 --- a/src/vs/editor/common/core/edits/arrayEdit.ts +++ b/src/vs/editor/common/core/edits/arrayEdit.ts @@ -33,7 +33,7 @@ export class ArrayEdit extends BaseEdit, ArrayEdit> { return new ArrayEdit([new ArrayReplacement(range, [])]); } - override _createNew(replacements: readonly ArrayReplacement[]): ArrayEdit { + protected override _createNew(replacements: readonly ArrayReplacement[]): ArrayEdit { return new ArrayEdit(replacements); } diff --git a/src/vs/editor/common/core/edits/edit.ts b/src/vs/editor/common/core/edits/edit.ts index 28ba49030ce..9271cb6f1a1 100644 --- a/src/vs/editor/common/core/edits/edit.ts +++ b/src/vs/editor/common/core/edits/edit.ts @@ -251,7 +251,7 @@ export class Edit> extends BaseEdit> { return new Edit([replacement]); } - override _createNew(replacements: readonly T[]): Edit { + protected override _createNew(replacements: readonly T[]): Edit { return new Edit(replacements); } } diff --git a/src/vs/editor/common/core/edits/lengthEdit.ts b/src/vs/editor/common/core/edits/lengthEdit.ts index c8cad96f43c..086c0780c49 100644 --- a/src/vs/editor/common/core/edits/lengthEdit.ts +++ b/src/vs/editor/common/core/edits/lengthEdit.ts @@ -23,7 +23,7 @@ export class LengthEdit extends BaseEdit { return new LengthEdit(edits); } - override _createNew(replacements: readonly LengthReplacement[]): LengthEdit { + protected override _createNew(replacements: readonly LengthReplacement[]): LengthEdit { return new LengthEdit(replacements); } } diff --git a/src/vs/editor/common/core/edits/stringEdit.ts b/src/vs/editor/common/core/edits/stringEdit.ts index 9b816307853..af00c31dd1d 100644 --- a/src/vs/editor/common/core/edits/stringEdit.ts +++ b/src/vs/editor/common/core/edits/stringEdit.ts @@ -33,7 +33,7 @@ export class StringEdit extends BaseEdit { return new StringEdit([new StringReplacement(range, '')]); } - override _createNew(replacements: readonly StringReplacement[]): StringEdit { + protected override _createNew(replacements: readonly StringReplacement[]): StringEdit { return new StringEdit(replacements); } From dc07b8e738e2207cd1c20ffd6c6a305e46bec217 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 7 May 2025 11:03:40 +0000 Subject: [PATCH 74/90] SCM - disable gutter menu in the quick diff peek widget (#248298) --- .../contrib/scm/browser/quickDiffWidget.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index 0ed0b86f0fd..9df41d9090e 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -376,7 +376,15 @@ class QuickDiffWidget extends PeekViewWidget { protected _fillBody(container: HTMLElement): void { const options: IDiffEditorOptions = { - scrollBeyondLastLine: true, + diffAlgorithm: 'advanced', + fixedOverflowWidgets: true, + ignoreTrimWhitespace: false, + minimap: { enabled: false }, + overviewRulerLanes: 2, + readOnly: false, + renderGutterMenu: false, + renderIndicators: false, + renderSideBySide: false, scrollbar: { verticalScrollbarSize: 14, horizontal: 'auto', @@ -384,14 +392,7 @@ class QuickDiffWidget extends PeekViewWidget { verticalHasArrows: false, horizontalHasArrows: false }, - overviewRulerLanes: 2, - fixedOverflowWidgets: true, - minimap: { enabled: false }, - renderSideBySide: false, - readOnly: false, - renderIndicators: false, - diffAlgorithm: 'advanced', - ignoreTrimWhitespace: false, + scrollBeyondLastLine: true, stickyScroll: { enabled: false } }; From 683ece74489047036a0ae32e0abca2f555b1150e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 7 May 2025 13:10:09 +0200 Subject: [PATCH 75/90] use resource api for all get extensions requests with experiment (#248299) --- .../common/extensionGalleryService.ts | 10 ++++++---- .../extensionManagement/common/extensionManagement.ts | 3 +-- .../extensions/browser/extensions.contribution.ts | 9 +-------- .../extensions/browser/extensionsWorkbenchService.ts | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index c6efadb3ccc..4f94e0906cb 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -16,7 +16,7 @@ import { URI } from '../../../base/common/uri.js'; import { IHeaders, IRequestContext, IRequestOptions, isOfflineError } from '../../../base/parts/request/common/request.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; import { IEnvironmentService } from '../../environment/common/environment.js'; -import { getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo, ISearchPrefferedResults, ExtensionGalleryError, ExtensionGalleryErrorCode, IProductVersion, UseUnpkgResourceApiConfigKey, IAllowedExtensionsService, EXTENSION_IDENTIFIER_REGEX, SortBy, FilterType, MaliciousExtensionInfo } from './extensionManagement.js'; +import { getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo, ISearchPrefferedResults, ExtensionGalleryError, ExtensionGalleryErrorCode, IProductVersion, IAllowedExtensionsService, EXTENSION_IDENTIFIER_REGEX, SortBy, FilterType, MaliciousExtensionInfo } from './extensionManagement.js'; import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from './extensionManagementUtil.js'; import { IExtensionManifest, TargetPlatform } from '../../extensions/common/extensions.js'; import { areApiProposalsCompatible, isEngineValid } from '../../extensions/common/extensionValidator.js'; @@ -592,7 +592,7 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle const options = CancellationToken.isCancellationToken(arg1) ? {} : arg1 as IExtensionQueryOptions; const token = CancellationToken.isCancellationToken(arg1) ? arg1 : arg2 as CancellationToken; - const resourceApi = (options.preferResourceApi && (this.configurationService.getValue(UseUnpkgResourceApiConfigKey) ?? false)) ? await this.getResourceApi(extensionGalleryManifest) : undefined; + const resourceApi = await this.getResourceApi(extensionGalleryManifest, !!options.updateCheck); const result = resourceApi ? await this.getExtensionsUsingResourceApi(extensionInfos, options, resourceApi, extensionGalleryManifest, token) : await this.getExtensionsUsingQueryApi(extensionInfos, options, extensionGalleryManifest, token); @@ -624,7 +624,7 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle return result; } - private async getResourceApi(extensionGalleryManifest: IExtensionGalleryManifest): Promise<{ uri: string; fallback?: string } | undefined> { + private async getResourceApi(extensionGalleryManifest: IExtensionGalleryManifest, updateCheck: boolean): Promise<{ uri: string; fallback?: string } | undefined> { const latestVersionResource = getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionLatestVersionUri); if (!latestVersionResource) { return undefined; @@ -637,7 +637,9 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle }; } - const value = await this.assignmentService?.getTreatment<'unpkg' | 'marketplace' | 'none'>('extensions.gallery.useResourceApi') ?? 'marketplace'; + const value = updateCheck + ? await this.assignmentService?.getTreatment<'unpkg' | 'marketplace' | 'none'>('extensions.gallery.useResourceApi') ?? 'marketplace' + : await this.assignmentService?.getTreatment<'unpkg' | 'marketplace' | 'none'>('extensions.gallery.useLatestApi') ?? 'unpkg'; if (value === 'marketplace') { return { diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 109607b695c..e91310f1cc7 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -382,7 +382,7 @@ export interface IExtensionQueryOptions { compatible?: boolean; queryAllVersions?: boolean; source?: string; - preferResourceApi?: boolean; + updateCheck?: boolean; } export interface IExtensionGalleryCapabilities { @@ -703,7 +703,6 @@ export async function computeSize(location: URI, fileService: IFileService): Pro export const ExtensionsLocalizedLabel = localize2('extensions', "Extensions"); export const PreferencesLocalizedLabel = localize2('preferences', 'Preferences'); -export const UseUnpkgResourceApiConfigKey = 'extensions.gallery.useUnpkgResourceApi'; export const AllowedExtensionsConfigKey = 'extensions.allowed'; export const VerifyExtensionSignatureConfigKey = 'extensions.verifySignature'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 39859b4a915..45a65a6455f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -8,7 +8,7 @@ import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { MenuRegistry, MenuId, registerAction2, Action2, IMenuItem, IAction2Options } from '../../../../platform/actions/common/actions.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { ExtensionsLocalizedLabel, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, UseUnpkgResourceApiConfigKey, SortBy, FilterType, VerifyExtensionSignatureConfigKey } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { ExtensionsLocalizedLabel, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, SortBy, FilterType, VerifyExtensionSignatureConfigKey } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { EnablementState, IExtensionManagementServerService, IPublisherInfo, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js'; import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; @@ -275,13 +275,6 @@ Registry.as(ConfigurationExtensions.Configuration) default: false, included: product.quality !== 'stable' }, - [UseUnpkgResourceApiConfigKey]: { - type: 'boolean', - description: localize('extensions.gallery.useUnpkgResourceApi', "When enabled, extensions to update are fetched from Unpkg service."), - default: true, - scope: ConfigurationScope.APPLICATION, - tags: ['onExp', 'usesOnlineServices'] - }, [ExtensionGalleryServiceUrlConfigKey]: { type: 'string', description: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 6187e1e600f..367c7ee8ca9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1873,7 +1873,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension count: infos.length, }); this.logService.trace(`Checking updates for extensions`, infos.map(e => e.id).join(', ')); - const galleryExtensions = await this.galleryService.getExtensions(infos, { targetPlatform, compatible: true, productVersion: this.getProductVersion(), preferResourceApi: true }, CancellationToken.None); + const galleryExtensions = await this.galleryService.getExtensions(infos, { targetPlatform, compatible: true, productVersion: this.getProductVersion(), updateCheck: true }, CancellationToken.None); if (galleryExtensions.length) { await this.syncInstalledExtensionsWithGallery(galleryExtensions); } From 014c000f81761f81460ddce4db18f4bee39072c5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 12:59:52 +0200 Subject: [PATCH 76/90] Moves common/core range datastructures to common/core/ranges --- src/vs/editor/browser/observableCodeEditor.ts | 2 +- src/vs/editor/browser/services/editorWorkerService.ts | 2 +- .../widget/diffEditor/components/accessibleDiffViewer.ts | 2 +- .../components/diffEditorViewZones/diffEditorViewZones.ts | 2 +- .../browser/widget/diffEditor/diffEditorViewModel.ts | 2 +- .../widget/diffEditor/diffProviderFactoryService.ts | 2 +- .../browser/widget/diffEditor/features/gutterFeature.ts | 2 +- .../diffEditor/features/hideUnchangedRegionsFeature.ts | 2 +- .../widget/diffEditor/features/movedBlocksLinesFeature.ts | 2 +- .../widget/diffEditor/features/overviewRulerFeature.ts | 2 +- .../widget/diffEditor/features/revertButtonsFeature.ts | 2 +- .../browser/widget/diffEditor/utils/editorGutter.ts | 2 +- src/vs/editor/common/core/lineEdit.ts | 2 +- src/vs/editor/common/core/{ => ranges}/columnRange.ts | 6 +++--- src/vs/editor/common/core/{ => ranges}/lineRange.ts | 8 ++++---- src/vs/editor/common/core/{ => ranges}/rangeMapping.ts | 8 ++++---- src/vs/editor/common/core/{ => ranges}/rangeSingleLine.ts | 5 ++++- src/vs/editor/common/core/textEdit.ts | 2 +- src/vs/editor/common/core/textLength.ts | 2 +- .../diff/defaultLinesDiffComputer/computeMovedLines.ts | 2 +- .../defaultLinesDiffComputer/defaultLinesDiffComputer.ts | 2 +- .../editor/common/diff/defaultLinesDiffComputer/utils.ts | 2 +- src/vs/editor/common/diff/legacyLinesDiffComputer.ts | 2 +- src/vs/editor/common/diff/rangeMapping.ts | 2 +- src/vs/editor/common/model/textModelTokens.ts | 2 +- src/vs/editor/common/model/tokenizationTextModelPart.ts | 2 +- src/vs/editor/common/model/tokens.ts | 2 +- src/vs/editor/common/tokens/contiguousMultilineTokens.ts | 2 +- .../contrib/diffEditorBreadcrumbs/browser/contribution.ts | 2 +- .../contrib/inlineCompletions/browser/model/ghostText.ts | 2 +- .../browser/model/inlineCompletionsModel.ts | 2 +- .../browser/view/ghostText/ghostTextView.ts | 4 ++-- .../view/inlineEdits/components/gutterIndicatorView.ts | 2 +- .../browser/view/inlineEdits/inlineEditWithChanges.ts | 2 +- .../browser/view/inlineEdits/inlineEditsModel.ts | 2 +- .../browser/view/inlineEdits/inlineEditsView.ts | 2 +- .../browser/view/inlineEdits/inlineEditsViewProducer.ts | 2 +- .../inlineEdits/inlineEditsViews/inlineEditsCustomView.ts | 2 +- .../inlineEditsViews/inlineEditsDeletionView.ts | 2 +- .../inlineEditsViews/inlineEditsInsertionView.ts | 2 +- .../inlineEditsViews/inlineEditsLineReplacementView.ts | 2 +- .../browser/view/inlineEdits/utils/utils.ts | 2 +- .../editor/test/browser/widget/diffEditorWidget.test.ts | 2 +- src/vs/editor/test/common/core/lineRange.test.ts | 2 +- .../chatEditing/chatEditingCodeEditorIntegration.ts | 2 +- .../chatEditing/chatEditingModifiedNotebookEntry.ts | 2 +- .../notebook/chatEditingNotebookEditorIntegration.ts | 2 +- .../contrib/inlineChat/browser/inlineChatSession.ts | 2 +- .../contrib/inlineChat/browser/inlineChatStrategies.ts | 2 +- .../contrib/inlineChat/browser/inlineChatWidget.ts | 2 +- .../contrib/inlineChat/test/browser/testWorkerService.ts | 2 +- .../contrib/mergeEditor/browser/model/diffComputer.ts | 2 +- .../textMateWorkerTokenizerController.ts | 2 +- .../worker/textMateWorkerTokenizer.ts | 2 +- 54 files changed, 66 insertions(+), 63 deletions(-) rename src/vs/editor/common/core/{ => ranges}/columnRange.ts (89%) rename src/vs/editor/common/core/{ => ranges}/lineRange.ts (98%) rename src/vs/editor/common/core/{ => ranges}/rangeMapping.ts (91%) rename src/vs/editor/common/core/{ => ranges}/rangeSingleLine.ts (91%) diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index bfdbb5b8238..1bb9fc7318c 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -7,7 +7,7 @@ import { equalsIfDefined, itemsEquals } from '../../base/common/equals.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js'; import { IObservable, IObservableWithChange, ITransaction, TransactionImpl, autorun, autorunOpts, derived, derivedOpts, derivedWithSetter, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.js'; import { EditorOption, FindComputedEditorOptionValueById } from '../common/config/editorOptions.js'; -import { LineRange } from '../common/core/lineRange.js'; +import { LineRange } from '../common/core/ranges/lineRange.js'; import { OffsetRange } from '../common/core/offsetRange.js'; import { Position } from '../common/core/position.js'; import { Selection } from '../common/core/selection.js'; diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index f19765d9397..76ed0450a63 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -27,7 +27,7 @@ import { IChange } from '../../common/diff/legacyLinesDiffComputer.js'; import { IDocumentDiff, IDocumentDiffProviderOptions } from '../../common/diff/documentDiffProvider.js'; import { ILinesDiffComputerOptions, MovedText } from '../../common/diff/linesDiffComputer.js'; import { DetailedLineRangeMapping, RangeMapping, LineRangeMapping } from '../../common/diff/rangeMapping.js'; -import { LineRange } from '../../common/core/lineRange.js'; +import { LineRange } from '../../common/core/ranges/lineRange.js'; import { SectionHeader, FindSectionHeaderOptions } from '../../common/services/findSectionHeaders.js'; import { mainWindow } from '../../../base/browser/window.js'; import { WindowIntervalTimer } from '../../../base/browser/dom.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts index 8a1c508df03..d6ea2d268bc 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts @@ -16,7 +16,7 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { applyFontInfo } from '../../../config/domFontInfo.js'; import { applyStyle } from '../utils.js'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts index 634939aacee..b8ea4caae2e 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts @@ -21,7 +21,7 @@ import { InlineDiffDeletedCodeMargin } from './inlineDiffDeletedCodeMargin.js'; import { LineSource, RenderOptions, renderLines } from './renderLines.js'; import { IObservableViewZone, animatedObservable, joinCombine } from '../../utils.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../../common/core/position.js'; import { DetailedLineRangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { ScrollType } from '../../../../../common/editorCommon.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index 25a79288cc2..660c989c145 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -10,7 +10,7 @@ import { IObservable, IReader, ISettableObservable, ITransaction, autorun, autor import { IDiffProviderFactoryService } from './diffProviderFactoryService.js'; import { filterWithPrevious } from './utils.js'; import { readHotReloadableExport } from '../../../../base/common/hotReloadHelpers.js'; -import { ISerializedLineRange, LineRange, LineRangeSet } from '../../../common/core/lineRange.js'; +import { ISerializedLineRange, LineRange, LineRangeSet } from '../../../common/core/ranges/lineRange.js'; import { DefaultLinesDiffComputer } from '../../../common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.js'; import { IDocumentDiff } from '../../../common/diff/documentDiffProvider.js'; import { MovedText } from '../../../common/diff/linesDiffComputer.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts b/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts index ab087b4ffb6..78d6e9b37b7 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts @@ -9,7 +9,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; -import { LineRange } from '../../../common/core/lineRange.js'; +import { LineRange } from '../../../common/core/ranges/lineRange.js'; import { IDocumentDiff, IDocumentDiffProvider, IDocumentDiffProviderOptions } from '../../../common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; import { ITextModel } from '../../../common/model.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts index 0d8b665b9e4..1187928002c 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts @@ -17,7 +17,7 @@ import { IContextKeyService } from '../../../../../platform/contextkey/common/co import { WorkbenchHoverDelegate } from '../../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { LineRange, LineRangeSet } from '../../../../common/core/lineRange.js'; +import { LineRange, LineRangeSet } from '../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Range } from '../../../../common/core/range.js'; import { TextEdit } from '../../../../common/core/textEdit.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts index e87fe47e055..8212330fc5c 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts @@ -14,7 +14,7 @@ import { isDefined } from '../../../../../base/common/types.js'; import { localize } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; import { CursorChangeReason } from '../../../../common/cursorEvents.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts index cbf57933eb5..49cb49b5796 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts @@ -17,7 +17,7 @@ import { DiffEditorEditors } from '../components/diffEditorEditors.js'; import { DiffEditorViewModel } from '../diffEditorViewModel.js'; import { PlaceholderViewZone, ViewZoneOverlayWidget, applyStyle, applyViewZones } from '../utils.js'; import { EditorLayoutInfo } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../common/core/ranges/lineRange.js'; import { OffsetRange, OffsetRangeSet } from '../../../../common/core/offsetRange.js'; import { MovedText } from '../../../../common/diff/linesDiffComputer.js'; import { localize } from '../../../../../nls.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts index 1db18e1478b..146e124ff38 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts @@ -15,7 +15,7 @@ import { DiffEditorEditors } from '../components/diffEditorEditors.js'; import { DiffEditorViewModel } from '../diffEditorViewModel.js'; import { appendRemoveOnDispose } from '../utils.js'; import { EditorLayoutInfo, EditorOption } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../common/core/position.js'; import { OverviewRulerZone } from '../../../../common/viewModel/overviewZoneManager.js'; import { defaultInsertColor, defaultRemoveColor, diffInserted, diffOverviewRulerInserted, diffOverviewRulerRemoved, diffRemoved } from '../../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts index 2d94b6e0657..76c1c664896 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts @@ -13,7 +13,7 @@ import { DiffEditorEditors } from '../components/diffEditorEditors.js'; import { DiffEditorOptions } from '../diffEditorOptions.js'; import { DiffEditorViewModel } from '../diffEditorViewModel.js'; import { DiffEditorWidget } from '../diffEditorWidget.js'; -import { LineRange, LineRangeSet } from '../../../../common/core/lineRange.js'; +import { LineRange, LineRangeSet } from '../../../../common/core/ranges/lineRange.js'; import { Range } from '../../../../common/core/range.js'; import { LineRangeMapping, RangeMapping } from '../../../../common/diff/rangeMapping.js'; import { GlyphMarginLane } from '../../../../common/model.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts b/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts index bc149ec17ea..ee601aa83e1 100644 --- a/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts +++ b/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts @@ -7,7 +7,7 @@ import { h, reset } from '../../../../../base/browser/dom.js'; import { Disposable, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun, IObservable, IReader, ISettableObservable, observableFromEvent, observableSignal, observableSignalFromEvent, observableValue, transaction } from '../../../../../base/common/observable.js'; import { CodeEditorWidget } from '../../codeEditor/codeEditorWidget.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; export class EditorGutter extends Disposable { diff --git a/src/vs/editor/common/core/lineEdit.ts b/src/vs/editor/common/core/lineEdit.ts index 507bd7b0f33..d05709f2b79 100644 --- a/src/vs/editor/common/core/lineEdit.ts +++ b/src/vs/editor/common/core/lineEdit.ts @@ -6,7 +6,7 @@ import { compareBy, groupAdjacentBy, numberComparator } from '../../../base/common/arrays.js'; import { assert, checkAdjacentItems } from '../../../base/common/assert.js'; import { splitLines } from '../../../base/common/strings.js'; -import { LineRange } from './lineRange.js'; +import { LineRange } from './ranges/lineRange.js'; import { OffsetEdit, SingleOffsetEdit } from './offsetEdit.js'; import { Position } from './position.js'; import { Range } from './range.js'; diff --git a/src/vs/editor/common/core/columnRange.ts b/src/vs/editor/common/core/ranges/columnRange.ts similarity index 89% rename from src/vs/editor/common/core/columnRange.ts rename to src/vs/editor/common/core/ranges/columnRange.ts index e955587a294..fec6b35219a 100644 --- a/src/vs/editor/common/core/columnRange.ts +++ b/src/vs/editor/common/core/ranges/columnRange.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { OffsetRange } from './offsetRange.js'; -import { Range } from './range.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetRange } from '../offsetRange.js'; +import { Range } from '../range.js'; /** * Represents a 1-based range of columns. diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/ranges/lineRange.ts similarity index 98% rename from src/vs/editor/common/core/lineRange.ts rename to src/vs/editor/common/core/ranges/lineRange.ts index b4d7a39da19..1eacb1d54ac 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/ranges/lineRange.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { OffsetRange } from './offsetRange.js'; -import { Range } from './range.js'; -import { findFirstIdxMonotonousOrArrLen, findLastIdxMonotonous, findLastMonotonous } from '../../../base/common/arraysFind.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetRange } from '../offsetRange.js'; +import { Range } from '../range.js'; +import { findFirstIdxMonotonousOrArrLen, findLastIdxMonotonous, findLastMonotonous } from '../../../../base/common/arraysFind.js'; /** * A range of lines (1-based). diff --git a/src/vs/editor/common/core/rangeMapping.ts b/src/vs/editor/common/core/ranges/rangeMapping.ts similarity index 91% rename from src/vs/editor/common/core/rangeMapping.ts rename to src/vs/editor/common/core/ranges/rangeMapping.ts index 8d838cc1f75..a6a80bbfc89 100644 --- a/src/vs/editor/common/core/rangeMapping.ts +++ b/src/vs/editor/common/core/ranges/rangeMapping.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findLastMonotonous } from '../../../base/common/arraysFind.js'; -import { Position } from './position.js'; -import { Range } from './range.js'; -import { TextLength } from './textLength.js'; +import { findLastMonotonous } from '../../../../base/common/arraysFind.js'; +import { Position } from '../position.js'; +import { Range } from '../range.js'; +import { TextLength } from '../textLength.js'; /** * Represents a list of mappings of ranges from one document to another. diff --git a/src/vs/editor/common/core/rangeSingleLine.ts b/src/vs/editor/common/core/ranges/rangeSingleLine.ts similarity index 91% rename from src/vs/editor/common/core/rangeSingleLine.ts rename to src/vs/editor/common/core/ranges/rangeSingleLine.ts index d16cf24b0dd..bb6bf71cf70 100644 --- a/src/vs/editor/common/core/rangeSingleLine.ts +++ b/src/vs/editor/common/core/ranges/rangeSingleLine.ts @@ -4,8 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { ColumnRange } from './columnRange.js'; -import { Range } from './range.js'; +import { Range } from '../range.js'; +/** + * Represents a column range in a single line. +*/ export class RangeSingleLine { public static fromRange(range: Range): RangeSingleLine | undefined { if (range.endLineNumber !== range.startLineNumber) { diff --git a/src/vs/editor/common/core/textEdit.ts b/src/vs/editor/common/core/textEdit.ts index 80d03aa1e34..9b11b2f95a1 100644 --- a/src/vs/editor/common/core/textEdit.ts +++ b/src/vs/editor/common/core/textEdit.ts @@ -8,7 +8,7 @@ import { assert, assertFn, checkAdjacentItems } from '../../../base/common/asser import { BugIndicatingError } from '../../../base/common/errors.js'; import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../base/common/strings.js'; import { ISingleEditOperation } from './editOperation.js'; -import { LineRange } from './lineRange.js'; +import { LineRange } from './ranges/lineRange.js'; import { OffsetEdit } from './offsetEdit.js'; import { Position } from './position.js'; import { PositionOffsetTransformer } from './positionToOffset.js'; diff --git a/src/vs/editor/common/core/textLength.ts b/src/vs/editor/common/core/textLength.ts index a1f2d8e35c8..b063b099f83 100644 --- a/src/vs/editor/common/core/textLength.ts +++ b/src/vs/editor/common/core/textLength.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LineRange } from './lineRange.js'; +import { LineRange } from './ranges/lineRange.js'; import { Position } from './position.js'; import { Range } from './range.js'; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts index 523486d84f1..20e1550aca1 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts @@ -8,7 +8,7 @@ import { DetailedLineRangeMapping, LineRangeMapping } from '../rangeMapping.js'; import { pushMany, compareBy, numberComparator, reverseOrder } from '../../../../base/common/arrays.js'; import { MonotonousArray, findLastMonotonous } from '../../../../base/common/arraysFind.js'; import { SetMap } from '../../../../base/common/map.js'; -import { LineRange, LineRangeSet } from '../../core/lineRange.js'; +import { LineRange, LineRangeSet } from '../../core/ranges/lineRange.js'; import { LinesSliceCharSequence } from './linesSliceCharSequence.js'; import { LineRangeFragment, isSpace } from './utils.js'; import { MyersDiffAlgorithm } from './algorithms/myersDiffAlgorithm.js'; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts index 884cd838698..9b35229cb85 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts @@ -5,7 +5,7 @@ import { equals } from '../../../../base/common/arrays.js'; import { assertFn } from '../../../../base/common/assert.js'; -import { LineRange } from '../../core/lineRange.js'; +import { LineRange } from '../../core/ranges/lineRange.js'; import { OffsetRange } from '../../core/offsetRange.js'; import { Position } from '../../core/position.js'; import { Range } from '../../core/range.js'; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts index cc5e19a2c93..1d41b64ac2f 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from '../../../../base/common/charCode.js'; -import { LineRange } from '../../core/lineRange.js'; +import { LineRange } from '../../core/ranges/lineRange.js'; import { DetailedLineRangeMapping } from '../rangeMapping.js'; export class Array2D { diff --git a/src/vs/editor/common/diff/legacyLinesDiffComputer.ts b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts index f3129ac9a7f..bbd79c0e274 100644 --- a/src/vs/editor/common/diff/legacyLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts @@ -10,7 +10,7 @@ import { RangeMapping, DetailedLineRangeMapping } from './rangeMapping.js'; import * as strings from '../../../base/common/strings.js'; import { Range } from '../core/range.js'; import { assertFn, checkAdjacentItems } from '../../../base/common/assert.js'; -import { LineRange } from '../core/lineRange.js'; +import { LineRange } from '../core/ranges/lineRange.js'; const MINIMUM_MATCHING_CHARACTER_LENGTH = 3; diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts index f6565bdc4e2..dbe71b0e7ad 100644 --- a/src/vs/editor/common/diff/rangeMapping.ts +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -6,7 +6,7 @@ import { groupAdjacentBy } from '../../../base/common/arrays.js'; import { assertFn, checkAdjacentItems } from '../../../base/common/assert.js'; import { BugIndicatingError } from '../../../base/common/errors.js'; -import { LineRange } from '../core/lineRange.js'; +import { LineRange } from '../core/ranges/lineRange.js'; import { Position } from '../core/position.js'; import { Range } from '../core/range.js'; import { AbstractText, SingleTextEdit, TextEdit } from '../core/textEdit.js'; diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 3a748495b9b..d1b6fee80fe 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -8,7 +8,7 @@ import { BugIndicatingError, onUnexpectedError } from '../../../base/common/erro import { setTimeout0 } from '../../../base/common/platform.js'; import { StopWatch } from '../../../base/common/stopwatch.js'; import { countEOL } from '../core/misc/eolCounter.js'; -import { LineRange } from '../core/lineRange.js'; +import { LineRange } from '../core/ranges/lineRange.js'; import { OffsetRange } from '../core/offsetRange.js'; import { Position } from '../core/position.js'; import { StandardTokenType } from '../encodedTokenAttributes.js'; diff --git a/src/vs/editor/common/model/tokenizationTextModelPart.ts b/src/vs/editor/common/model/tokenizationTextModelPart.ts index f0bc53336ac..b1e8573652c 100644 --- a/src/vs/editor/common/model/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/model/tokenizationTextModelPart.ts @@ -8,7 +8,7 @@ import { BugIndicatingError, onUnexpectedError } from '../../../base/common/erro import { Emitter, Event } from '../../../base/common/event.js'; import { DisposableMap, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js'; import { countEOL } from '../core/misc/eolCounter.js'; -import { LineRange } from '../core/lineRange.js'; +import { LineRange } from '../core/ranges/lineRange.js'; import { IPosition, Position } from '../core/position.js'; import { Range } from '../core/range.js'; import { IWordAtPosition, getWordAtText } from '../core/wordHelper.js'; diff --git a/src/vs/editor/common/model/tokens.ts b/src/vs/editor/common/model/tokens.ts index ad99df1f29b..3cd0c70364e 100644 --- a/src/vs/editor/common/model/tokens.ts +++ b/src/vs/editor/common/model/tokens.ts @@ -7,7 +7,7 @@ import { equals } from '../../../base/common/arrays.js'; import { RunOnceScheduler } from '../../../base/common/async.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable } from '../../../base/common/lifecycle.js'; -import { LineRange } from '../core/lineRange.js'; +import { LineRange } from '../core/ranges/lineRange.js'; import { StandardTokenType } from '../encodedTokenAttributes.js'; import { ILanguageIdCodec } from '../languages.js'; import { IAttachedView } from '../model.js'; diff --git a/src/vs/editor/common/tokens/contiguousMultilineTokens.ts b/src/vs/editor/common/tokens/contiguousMultilineTokens.ts index 5a360be1c7c..7f47ae16581 100644 --- a/src/vs/editor/common/tokens/contiguousMultilineTokens.ts +++ b/src/vs/editor/common/tokens/contiguousMultilineTokens.ts @@ -9,7 +9,7 @@ import { Position } from '../core/position.js'; import { IRange } from '../core/range.js'; import { countEOL } from '../core/misc/eolCounter.js'; import { ContiguousTokensEditing } from './contiguousTokensEditing.js'; -import { LineRange } from '../core/lineRange.js'; +import { LineRange } from '../core/ranges/lineRange.js'; /** * Represents contiguous tokens over a contiguous range of lines. diff --git a/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts b/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts index 0eb179dc3b0..ee9be220430 100644 --- a/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts +++ b/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts @@ -7,7 +7,7 @@ import { reverseOrder, compareBy, numberComparator } from '../../../../base/comm import { observableValue, observableSignalFromEvent, autorunWithStore, IReader } from '../../../../base/common/observable.js'; import { HideUnchangedRegionsFeature, IDiffEditorBreadcrumbsSource } from '../../../browser/widget/diffEditor/features/hideUnchangedRegionsFeature.js'; import { DisposableCancellationTokenSource } from '../../../browser/widget/diffEditor/utils.js'; -import { LineRange } from '../../../common/core/lineRange.js'; +import { LineRange } from '../../../common/core/ranges/lineRange.js'; import { ITextModel } from '../../../common/model.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; import { IOutlineModelService, OutlineModel } from '../../documentSymbols/browser/outlineModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts index 26fd510b518..f74a1e1a90e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts @@ -10,7 +10,7 @@ import { Range } from '../../../../common/core/range.js'; import { SingleTextEdit, TextEdit } from '../../../../common/core/textEdit.js'; import { LineDecoration } from '../../../../common/viewLayout/lineDecorations.js'; import { InlineDecoration } from '../../../../common/viewModel.js'; -import { ColumnRange } from '../../../../common/core/columnRange.js'; +import { ColumnRange } from '../../../../common/core/ranges/columnRange.js'; export class GhostText { constructor( diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index b92e6b7ba44..a1948beefa1 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -19,7 +19,7 @@ import { observableCodeEditor } from '../../../../browser/observableCodeEditor.j import { EditorOption } from '../../../../common/config/editorOptions.js'; import { CursorColumns } from '../../../../common/core/cursorColumns.js'; import { EditOperation } from '../../../../common/core/editOperation.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; import { Selection } from '../../../../common/core/selection.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts index a4215f06f8e..c26c6e3034c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts @@ -27,8 +27,8 @@ import { LineDecoration } from '../../../../../common/viewLayout/lineDecorations import { RenderLineInput, renderViewLine } from '../../../../../common/viewLayout/viewLineRenderer.js'; import { InlineDecorationType } from '../../../../../common/viewModel.js'; import { GhostText, GhostTextReplacement, IGhostTextLine } from '../../model/ghostText.js'; -import { RangeSingleLine } from '../../../../../common/core/rangeSingleLine.js'; -import { ColumnRange } from '../../../../../common/core/columnRange.js'; +import { RangeSingleLine } from '../../../../../common/core/ranges/rangeSingleLine.js'; +import { ColumnRange } from '../../../../../common/core/ranges/columnRange.js'; import { addDisposableListener, getWindow, isHTMLElement, n } from '../../../../../../base/browser/dom.js'; import './ghostTextView.css'; import { IMouseEvent, StandardMouseEvent } from '../../../../../../base/browser/mouseEvent.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts index 0ce8e0c18d3..651914fa457 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts @@ -21,7 +21,7 @@ import { Rect } from '../../../../../../common/core/2d/rect.js'; import { HoverService } from '../../../../../../browser/services/hoverService/hoverService.js'; import { HoverWidget } from '../../../../../../browser/services/hoverService/hoverWidget.js'; import { EditorOption, RenderLineNumbersType } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { StickyScrollController } from '../../../../../stickyScroll/browser/stickyScrollController.js'; import { IInlineEditModel, InlineEditTabAction } from '../inlineEditsViewInterface.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts index 10394bd1b22..2041582469a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { SingleLineEdit } from '../../../../../common/core/lineEdit.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../../common/core/position.js'; import { AbstractText, TextEdit } from '../../../../../common/core/textEdit.js'; import { Command } from '../../../../../common/languages.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts index 62275ba537a..6498fd5340a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts @@ -8,7 +8,7 @@ import { derived, IObservable } from '../../../../../../base/common/observable.j import { localize } from '../../../../../../nls.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { StringText, TextEdit } from '../../../../../common/core/textEdit.js'; import { Command, InlineCompletionDisplayLocation } from '../../../../../common/languages.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts index cdd3bd12518..6b1be8663d8 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts @@ -12,7 +12,7 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../../common/core/position.js'; import { Range } from '../../../../../common/core/range.js'; import { AbstractText, SingleTextEdit, StringText } from '../../../../../common/core/textEdit.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts index 69e0c7c1b32..d170cc6c437 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts @@ -9,7 +9,7 @@ import { derived, IObservable, ISettableObservable } from '../../../../../../bas import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { Range } from '../../../../../common/core/range.js'; import { SingleTextEdit, TextEdit } from '../../../../../common/core/textEdit.js'; import { TextModelText } from '../../../../../common/model/textModelText.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts index 09e36074a07..9a58349ef2f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsCustomView.ts @@ -15,7 +15,7 @@ import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../br import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; import { InlineCompletionDisplayLocation } from '../../../../../../common/languages.js'; import { ILanguageService } from '../../../../../../common/languages/language.js'; import { LineTokens } from '../../../../../../common/tokens/lineTokens.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts index 89bf9d86871..8ea6a771206 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts @@ -13,7 +13,7 @@ import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { Rect } from '../../../../../../common/core/2d/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Position } from '../../../../../../common/core/position.js'; import { Range } from '../../../../../../common/core/range.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts index 8ebcb209830..565ef554c0d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts @@ -15,7 +15,7 @@ import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../br import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Position } from '../../../../../../common/core/position.js'; import { Range } from '../../../../../../common/core/range.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts index 7df8e3af3bb..9a2021b6853 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts @@ -18,7 +18,7 @@ import { Point } from '../../../../../../common/core/2d/point.js'; import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Range } from '../../../../../../common/core/range.js'; import { ILanguageService } from '../../../../../../common/languages/language.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts index e07a71dab93..e2dc6695af6 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts @@ -18,7 +18,7 @@ import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEd import { Point } from '../../../../../../common/core/2d/point.js'; import { Rect } from '../../../../../../common/core/2d/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; +import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Position } from '../../../../../../common/core/position.js'; import { Range } from '../../../../../../common/core/range.js'; diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts index eeac9f526e5..1e293a9e878 100644 --- a/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts +++ b/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { UnchangedRegion } from '../../../browser/widget/diffEditor/diffEditorViewModel.js'; -import { LineRange } from '../../../common/core/lineRange.js'; +import { LineRange } from '../../../common/core/ranges/lineRange.js'; import { DetailedLineRangeMapping } from '../../../common/diff/rangeMapping.js'; suite('DiffEditorWidget2', () => { diff --git a/src/vs/editor/test/common/core/lineRange.test.ts b/src/vs/editor/test/common/core/lineRange.test.ts index 30832250039..d13ceb9fb20 100644 --- a/src/vs/editor/test/common/core/lineRange.test.ts +++ b/src/vs/editor/test/common/core/lineRange.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { LineRange, LineRangeSet } from '../../../common/core/lineRange.js'; +import { LineRange, LineRangeSet } from '../../../common/core/ranges/lineRange.js'; suite('LineRange', () => { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts index b91ea9bde5e..156b0102446 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts @@ -17,7 +17,7 @@ import { AccessibleDiffViewer, IAccessibleDiffViewerModel } from '../../../../.. import { RenderOptions, LineSource, renderLines } from '../../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { diffAddDecoration, diffWholeLineAddDecoration, diffDeleteDecoration } from '../../../../../editor/browser/widget/diffEditor/registrations.contribution.js'; import { EditorOption, IEditorOptions } from '../../../../../editor/common/config/editorOptions.js'; -import { LineRange } from '../../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; import { Position } from '../../../../../editor/common/core/position.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { Selection } from '../../../../../editor/common/core/selection.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts index 2df60310898..8fec90b3e67 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts @@ -14,7 +14,7 @@ import { isEqual } from '../../../../../base/common/resources.js'; import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; -import { LineRange } from '../../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts index ff79d9d3ea4..15abfc891db 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts @@ -7,7 +7,7 @@ import { Disposable, IDisposable, toDisposable } from '../../../../../../base/co import { autorun, debouncedObservable, IObservable, ISettableObservable, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; import { basename } from '../../../../../../base/common/resources.js'; import { assertType } from '../../../../../../base/common/types.js'; -import { LineRange } from '../../../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../../../editor/common/core/ranges/lineRange.js'; import { Range } from '../../../../../../editor/common/core/range.js'; import { nullDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; import { PrefixSumComputer } from '../../../../../../editor/common/model/prefixSumComputer.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index c153bb89559..751fb9c1728 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -12,7 +12,7 @@ import { ModelDecorationOptions } from '../../../../editor/common/model/textMode import { EditOperation, ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; import { IInlineChatSessionService } from './inlineChatSessionService.js'; -import { LineRange } from '../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../editor/common/core/ranges/lineRange.js'; import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; import { coalesceInPlace } from '../../../../base/common/arrays.js'; import { Iterable } from '../../../../base/common/iterator.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 1c19f39cd02..a2bcfa14a33 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -12,7 +12,7 @@ import { ICodeEditor, IViewZone, IViewZoneChangeAccessor } from '../../../../edi import { StableEditorScrollState } from '../../../../editor/browser/stableEditorScroll.js'; import { LineSource, RenderOptions, renderLines } from '../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; -import { LineRange } from '../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../editor/common/core/ranges/lineRange.js'; import { Position } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 6207826407e..6a6cd0f38b8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -14,7 +14,7 @@ import { constObservable, derived, IObservable, ISettableObservable, observableV import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { AccessibleDiffViewer, IAccessibleDiffViewerModel } from '../../../../editor/browser/widget/diffEditor/components/accessibleDiffViewer.js'; import { EditorOption, IComputedEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { LineRange } from '../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../editor/common/core/ranges/lineRange.js'; import { Position } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; import { Selection } from '../../../../editor/common/core/selection.js'; diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts index 416e3cf3a47..edfa6e50429 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts @@ -10,7 +10,7 @@ import { assertType } from '../../../../../base/common/types.js'; import { DiffAlgorithmName, IEditorWorkerService, ILineChange } from '../../../../../editor/common/services/editorWorker.js'; import { IDocumentDiff, IDocumentDiffProviderOptions } from '../../../../../editor/common/diff/documentDiffProvider.js'; import { EditorWorker } from '../../../../../editor/common/services/editorWebWorker.js'; -import { LineRange } from '../../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; import { MovedText } from '../../../../../editor/common/diff/linesDiffComputer.js'; import { LineRangeMapping, DetailedLineRangeMapping, RangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; import { TextEdit } from '../../../../../editor/common/languages.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts index 8bc16815852..94ee5e1c408 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts @@ -12,7 +12,7 @@ import { IConfigurationService } from '../../../../../platform/configuration/com import { LineRange } from './lineRange.js'; import { DetailedLineRangeMapping, RangeMapping } from './mapping.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; -import { LineRange as DiffLineRange } from '../../../../../editor/common/core/lineRange.js'; +import { LineRange as DiffLineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; export interface IMergeDiffComputer { computeDiff(textModel1: ITextModel, textModel2: ITextModel, reader: IReader): Promise; diff --git a/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts b/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts index e0d5a78cca2..2e96a0eddd6 100644 --- a/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts +++ b/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts @@ -8,7 +8,7 @@ import { Disposable } from '../../../../../base/common/lifecycle.js'; import { IObservable, autorun, keepObserved } from '../../../../../base/common/observable.js'; import { Proxied } from '../../../../../base/common/worker/webWorker.js'; import { countEOL } from '../../../../../editor/common/core/misc/eolCounter.js'; -import { LineRange } from '../../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { IBackgroundTokenizationStore, ILanguageIdCodec } from '../../../../../editor/common/languages.js'; import { ITextModel } from '../../../../../editor/common/model.js'; diff --git a/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts b/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts index f3945c5e8fa..fee62cac570 100644 --- a/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts +++ b/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts @@ -8,7 +8,7 @@ import { RunOnceScheduler } from '../../../../../../base/common/async.js'; import { observableValue } from '../../../../../../base/common/observable.js'; import { setTimeout0 } from '../../../../../../base/common/platform.js'; import { URI } from '../../../../../../base/common/uri.js'; -import { LineRange } from '../../../../../../editor/common/core/lineRange.js'; +import { LineRange } from '../../../../../../editor/common/core/ranges/lineRange.js'; import { LanguageId } from '../../../../../../editor/common/encodedTokenAttributes.js'; import { IModelChangedEvent, MirrorTextModel } from '../../../../../../editor/common/model/mirrorTextModel.js'; import { TokenizerWithStateStore } from '../../../../../../editor/common/model/textModelTokens.js'; From 55442e23e7671623c899e80ce988be94f70627a1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 7 May 2025 14:07:41 +0200 Subject: [PATCH 77/90] fix extension getting grayed out (#248303) --- src/vs/workbench/contrib/extensions/browser/extensionsList.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 97be5462cf6..0f063eb604b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -185,7 +185,7 @@ export class Renderer implements IPagedRenderer { data.extensionDisposables = dispose(data.extensionDisposables); const updateEnablement = () => { - const disabled = extension.state === ExtensionState.Installed && extension.local && !this.extensionEnablementService.isEnabled(extension.local); + const disabled = extension.state === ExtensionState.Installed && !!extension.local && !this.extensionEnablementService.isEnabled(extension.local); const deprecated = !!extension.deprecationInfo; data.element.classList.toggle('deprecated', deprecated); data.root.classList.toggle('disabled', disabled); From 1adb79116d101c73be7215732fe378fd92b462ba Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 May 2025 15:17:00 +0200 Subject: [PATCH 78/90] process explorer tweaks (#248309) --- .../electron-sandbox/media/processExplorer.css | 7 ++++++- .../electron-sandbox/processExplorerControl.ts | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css b/src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css index 0b617a8ea5a..9737faebbdf 100644 --- a/src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/media/processExplorer.css @@ -15,7 +15,7 @@ padding-right: 10px; } -.process-explorer .row:not(.header) .cell { +.process-explorer .row .cell { border-right: 1px solid var(--vscode-tree-tableColumnsBorder); } @@ -24,6 +24,11 @@ border-bottom: 1px solid var(--vscode-tree-tableColumnsBorder); } +.process-explorer .row.header .cell { + overflow: hidden; + text-overflow: ellipsis; +} + .process-explorer .row .cell.name { text-align: left; flex-grow: 1; diff --git a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts index a3fd6268701..5bfba8fc8e6 100644 --- a/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts +++ b/src/vs/workbench/contrib/processExplorer/electron-sandbox/processExplorerControl.ts @@ -137,8 +137,11 @@ class ProcessTreeDataSource implements IDataSource, index: number, templateData: IProcessItemTemplateData, height: number | undefined): void { From f6963b9cb970865c769b3dc2887a47e2c5e57f04 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 May 2025 15:22:45 +0200 Subject: [PATCH 79/90] debt - add `IChatModel#requestInProgressObs` and `IChatResponseModel#isInProgress` to prevent further bugs (#248310) * fixes https://github.com/microsoft/vscode/issues/248211 * debt - add `IChatModel#requestInProgressObs` and `IChatResponseModel#isInProgress` to prevent further bugs --- .../chatEditingEditorContextKeys.ts | 10 +--- .../chatEditing/chatEditingEditorOverlay.ts | 16 +---- .../chatEditingModifiedFileEntry.ts | 4 +- .../contrib/chat/common/chatModel.ts | 60 +++++++++++++------ 4 files changed, 46 insertions(+), 44 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts index 361997d3bcc..6e0c2fe0ae9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts @@ -113,18 +113,10 @@ class ContextKeyGroup { const chatModel = chatService.getSession(session.chatSessionId); - const lastResponse = chatModel - ? observableFromEvent(this, chatModel.onDidChange, () => chatModel.getRequests().at(-1)?.response).read(r) - : undefined; - - const isRequestInProgress = lastResponse - ? observableFromEvent(this, lastResponse.onDidChange, () => !lastResponse.isPendingConfirmation && !lastResponse.isComplete) - : constObservable(false); - this._ctxHasEditorModification.set(entry?.state.read(r) === ModifiedFileEntryState.Modified); this._ctxIsGlobalEditingSession.set(session.isGlobalEditingSession); this._ctxReviewModeEnabled.set(entry ? entry.reviewMode.read(r) : false); - this._ctxHasRequestInProgress.set(isRequestInProgress.read(r)); + this._ctxHasRequestInProgress.set(chatModel?.requestInProgressObs.read(r) ?? false); // number of requests const requestCount = chatModel diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts index afcda47869d..963d4ed9f12 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts @@ -59,13 +59,7 @@ class ChatEditorOverlayWidget extends Disposable { const session = this._session.read(r); const chatModel = session && _chatService.getSession(session?.chatSessionId); - const lastResponse = chatModel - ? observableFromEvent(this, chatModel.onDidChange, () => chatModel.getRequests().at(-1)?.response).read(r) - : undefined; - - return lastResponse - ? observableFromEvent(this, lastResponse.onDidChange, () => !lastResponse.isPendingConfirmation && !lastResponse.isComplete).read(r) - : false; + return chatModel?.requestInProgressObs.read(r) ?? false; }); const requestMessage = derived(r => { @@ -372,13 +366,7 @@ class ChatEditingOverlayController { } const chatModel = chatService.getSession(session.chatSessionId)!; - const lastResponse = observableFromEvent(this, chatModel.onDidChange, () => chatModel.getRequests().at(-1)?.response); - - const response = lastResponse.read(r); - if (!response) { - return false; - } - return observableFromEvent(this, response.onDidChange, () => !response.isComplete && !response.isPendingConfirmation).read(r); + return chatModel.requestInProgressObs.read(r); }); this._store.add(autorun(r => { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index b22f29e571f..d7757997f32 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts @@ -8,7 +8,7 @@ import { Emitter } from '../../../../../base/common/event.js'; import { Disposable, DisposableMap, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../../base/common/network.js'; import { clamp } from '../../../../../base/common/numbers.js'; -import { autorun, derived, IObservable, ITransaction, observableFromEvent, observableValue, observableValueOpts } from '../../../../../base/common/observable.js'; +import { autorun, derived, IObservable, ITransaction, observableValue, observableValueOpts } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; import { TextEdit } from '../../../../../editor/common/languages.js'; @@ -61,7 +61,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im readonly lastModifyingResponse: IObservable = this._lastModifyingResponseObs; protected readonly _lastModifyingResponseInProgressObs = this._lastModifyingResponseObs.map((value, r) => { - return value && observableFromEvent(this, value.onDidChange, () => !value.isComplete && !value.isPendingConfirmation).read(r); + return value?.isInProgress.read(r) ?? false; }); protected readonly _rewriteRatioObs = observableValue(this, 0); diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 42dd90f95db..bdde10e3a13 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -13,7 +13,7 @@ import { ResourceMap } from '../../../../base/common/map.js'; import { revive } from '../../../../base/common/marshalling.js'; import { Schemas } from '../../../../base/common/network.js'; import { equals } from '../../../../base/common/objects.js'; -import { IObservable, ITransaction, ObservablePromise, observableValue } from '../../../../base/common/observable.js'; +import { IObservable, ITransaction, observableFromEvent, ObservablePromise, observableSignalFromEvent, observableValue } from '../../../../base/common/observable.js'; import { basename, isEqual } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI, UriComponents, UriDto, isUriComponents } from '../../../../base/common/uri.js'; @@ -359,7 +359,8 @@ export interface IChatResponseModel { readonly isComplete: boolean; readonly isCanceled: boolean; readonly isPaused: IObservable; - readonly isPendingConfirmation: boolean; + readonly isPendingConfirmation: IObservable; + readonly isInProgress: IObservable; readonly shouldBeRemovedOnSend: IChatRequestDisablement | undefined; readonly isCompleteAddedRequest: boolean; /** A stale response is one that has been persisted and rehydrated, so e.g. Commands that have their arguments stored in the EH are gone. */ @@ -862,16 +863,14 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel return this._isStale; } - private _isPaused = observableValue('isPaused', false); + private readonly _isPaused = observableValue('isPaused', false); public get isPaused(): IObservable { return this._isPaused; } - public get isPendingConfirmation() { - return this._response.value.some(part => - part.kind === 'toolInvocation' && part.isConfirmed === undefined - || part.kind === 'confirmation' && part.isUsed === false); - } + readonly isPendingConfirmation: IObservable; + + readonly isInProgress: IObservable; private _responseView?: ResponseView; public get response(): IResponse { @@ -910,6 +909,28 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel this._isStale = Array.isArray(params.responseContent) && (params.responseContent.length !== 0 || isMarkdownString(params.responseContent) && params.responseContent.value.length !== 0); this._response = this._register(new Response(params.responseContent)); + + const signal = observableSignalFromEvent(this, this.onDidChange); + + this.isPendingConfirmation = signal.map((_value, r) => { + + signal.read(r); + + return this._response.value.some(part => + part.kind === 'toolInvocation' && part.isConfirmed === undefined + || part.kind === 'confirmation' && part.isUsed === false + ); + }); + + this.isInProgress = signal.map((_value, r) => { + + signal.read(r); + + return !this.isPendingConfirmation.read(r) + && !this.shouldBeRemovedOnSend + && !this._isComplete; + }); + this._register(this._response.onDidChangeValue(() => this._onDidChange.fire(defaultChatResponseModelChangeReason))); this.id = params.restoredId ?? 'response_' + generateUuid(); } @@ -1052,6 +1073,7 @@ export interface IChatModel { readonly title: string; readonly sampleQuestions: IChatFollowup[] | undefined; readonly requestInProgress: boolean; + readonly requestInProgressObs: IObservable; readonly requestPausibility: ChatPauseState; readonly inputPlaceholder?: string; readonly editingSessionObs?: ObservablePromise | undefined; @@ -1330,21 +1352,14 @@ export class ChatModel extends Disposable implements IChatModel { } get requestInProgress(): boolean { - const lastRequest = this.lastRequest; - if (!lastRequest?.response) { - return false; - } - - if (lastRequest.response.isPendingConfirmation) { - return false; - } - - return !lastRequest.response.isComplete; + return this.requestInProgressObs.get(); } + readonly requestInProgressObs: IObservable; + get requestPausibility(): ChatPauseState { const lastRequest = this.lastRequest; - if (!lastRequest?.response?.agent || lastRequest.response.isComplete || lastRequest.response.isPendingConfirmation) { + if (!lastRequest?.response?.agent || lastRequest.response.isComplete || lastRequest.response.isPendingConfirmation.get()) { return ChatPauseState.NotPausable; } @@ -1449,6 +1464,13 @@ export class ChatModel extends Disposable implements IChatModel { this._initialRequesterAvatarIconUri = initialData?.requesterAvatarIconUri && URI.revive(initialData.requesterAvatarIconUri); this._initialResponderAvatarIconUri = isUriComponents(initialData?.responderAvatarIconUri) ? URI.revive(initialData.responderAvatarIconUri) : initialData?.responderAvatarIconUri; + + + const lastResponse = observableFromEvent(this, this.onDidChange, () => this._requests.at(-1)?.response); + + this.requestInProgressObs = lastResponse.map((response, r) => { + return response?.isInProgress.read(r) ?? false; + }); } startEditingSession(isGlobalEditingSession?: boolean): void { From f63c3e0f4eb9a6f7b2c9203451b7c9270e87c542 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 7 May 2025 23:32:32 +1000 Subject: [PATCH 80/90] Use codemapper when applying edits to notebook (#246674) * Address code review comments * Updates * Updates * Updates * Updates * Updates * updates --- .../browser/actions/codeBlockOperations.ts | 75 ++++++++++++++++--- .../browser/inlineChatController.ts | 69 +++++++++++++++++ 2 files changed, 133 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts b/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts index 91581f38c9a..80758dba416 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts @@ -24,10 +24,10 @@ import { ILogService } from '../../../../../platform/log/common/log.js'; import { IProgressService, ProgressLocation } from '../../../../../platform/progress/common/progress.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { ITextFileService } from '../../../../services/textfile/common/textfiles.js'; -import { reviewEdits } from '../../../inlineChat/browser/inlineChatController.js'; +import { reviewEdits, reviewNotebookEdits } from '../../../inlineChat/browser/inlineChatController.js'; import { insertCell } from '../../../notebook/browser/controller/cellOperations.js'; import { IActiveNotebookEditor, INotebookEditor } from '../../../notebook/browser/notebookBrowser.js'; -import { CellKind, NOTEBOOK_EDITOR_ID } from '../../../notebook/common/notebookCommon.js'; +import { CellKind, ICellEditOperation, NOTEBOOK_EDITOR_ID } from '../../../notebook/common/notebookCommon.js'; import { ICodeMapperCodeBlock, ICodeMapperRequest, ICodeMapperResponse, ICodeMapperService } from '../../common/chatCodeMapperService.js'; import { ChatUserAction, IChatService } from '../../common/chatService.js'; import { isResponseVM } from '../../common/chatViewModel.js'; @@ -109,7 +109,6 @@ export class ApplyCodeBlockOperation { @IEditorService private readonly editorService: IEditorService, @ITextFileService private readonly textFileService: ITextFileService, @IChatService private readonly chatService: IChatService, - @ILanguageService private readonly languageService: ILanguageService, @IFileService private readonly fileService: IFileService, @IDialogService private readonly dialogService: IDialogService, @ILogService private readonly logService: ILogService, @@ -155,7 +154,7 @@ export class ApplyCodeBlockOperation { } else { const activeNotebookEditor = getActiveNotebookEditor(this.editorService); if (activeNotebookEditor) { - result = await this.handleNotebookEditor(activeNotebookEditor, context.code); + result = await this.handleNotebookEditor(activeNotebookEditor, context.chatSessionId, context.code); } else { this.notify(localize('applyCodeBlock.noActiveEditor', "To apply this code block, open a code or notebook editor.")); } @@ -215,15 +214,43 @@ export class ApplyCodeBlockOperation { return undefined; } - private async handleNotebookEditor(notebookEditor: IActiveNotebookEditor, code: string): Promise { + private async handleNotebookEditor(notebookEditor: IActiveNotebookEditor, chatSessionId: string | undefined, code: string): Promise { if (notebookEditor.isReadOnly) { this.notify(localize('applyCodeBlock.readonlyNotebook', "Cannot apply code block to read-only notebook editor.")); return undefined; } - const focusRange = notebookEditor.getFocus(); - const next = Math.max(focusRange.end - 1, 0); - insertCell(this.languageService, notebookEditor, next, CellKind.Code, 'below', code, true); - return undefined; + const uri = notebookEditor.textModel.uri; + const codeBlock = { code, resource: uri, markdownBeforeBlock: undefined }; + const codeMapper = this.codeMapperService.providers[0]?.displayName; + if (!codeMapper) { + this.notify(localize('applyCodeBlock.noCodeMapper', "No code mapper available.")); + return undefined; + } + let editsProposed = false; + const cancellationTokenSource = new CancellationTokenSource(); + try { + const iterable = await this.progressService.withProgress>( + { location: ProgressLocation.Notification, delay: 500, sticky: true, cancellable: true }, + async progress => { + progress.report({ message: localize('applyCodeBlock.progress', "Applying code block using {0}...", codeMapper) }); + const editsIterable = this.getNotebookEdits(codeBlock, chatSessionId, cancellationTokenSource.token); + return await this.waitForFirstElement(editsIterable); + }, + () => cancellationTokenSource.cancel() + ); + editsProposed = await this.applyNotebookEditsWithInlinePreview(iterable, uri, cancellationTokenSource); + } catch (e) { + if (!isCancellationError(e)) { + this.notify(localize('applyCodeBlock.error', "Failed to apply code block: {0}", e.message)); + } + } finally { + cancellationTokenSource.dispose(); + } + + return { + editsProposed, + codeMapper + }; } private async handleTextEditor(codeEditor: IActiveCodeEditor, chatSessionId: string | undefined, code: string): Promise { @@ -247,7 +274,7 @@ export class ApplyCodeBlockOperation { { location: ProgressLocation.Notification, delay: 500, sticky: true, cancellable: true }, async progress => { progress.report({ message: localize('applyCodeBlock.progress', "Applying code block using {0}...", codeMapper) }); - const editsIterable = this.getEdits(codeBlock, chatSessionId, cancellationTokenSource.token); + const editsIterable = this.getTextEdits(codeBlock, chatSessionId, cancellationTokenSource.token); return await this.waitForFirstElement(editsIterable); }, () => cancellationTokenSource.cancel() @@ -267,7 +294,7 @@ export class ApplyCodeBlockOperation { }; } - private getEdits(codeBlock: ICodeMapperCodeBlock, chatSessionId: string | undefined, token: CancellationToken): AsyncIterable { + private getTextEdits(codeBlock: ICodeMapperCodeBlock, chatSessionId: string | undefined, token: CancellationToken): AsyncIterable { return new AsyncIterableObject(async executor => { const request: ICodeMapperRequest = { codeBlocks: [codeBlock], @@ -288,6 +315,28 @@ export class ApplyCodeBlockOperation { }); } + private getNotebookEdits(codeBlock: ICodeMapperCodeBlock, chatSessionId: string | undefined, token: CancellationToken): AsyncIterable<[URI, TextEdit[]] | ICellEditOperation[]> { + return new AsyncIterableObject<[URI, TextEdit[]] | ICellEditOperation[]>(async executor => { + const request: ICodeMapperRequest = { + codeBlocks: [codeBlock], + chatSessionId, + location: 'panel' + }; + const response: ICodeMapperResponse = { + textEdit: (target: URI, edits: TextEdit[]) => { + executor.emitOne([target, edits]); + }, + notebookEdit(_resource, edit) { + executor.emitOne(edit); + }, + }; + const result = await this.codeMapperService.mapCode(request, response, token); + if (result?.errorMessage) { + executor.reject(new Error(result.errorMessage)); + } + }); + } + private async waitForFirstElement(iterable: AsyncIterable): Promise> { const iterator = iterable[Symbol.asyncIterator](); let result = await iterator.next(); @@ -314,6 +363,10 @@ export class ApplyCodeBlockOperation { return this.instantiationService.invokeFunction(reviewEdits, codeEditor, edits, tokenSource.token); } + private async applyNotebookEditsWithInlinePreview(edits: AsyncIterable<[URI, TextEdit[]] | ICellEditOperation[]>, uri: URI, tokenSource: CancellationTokenSource): Promise { + return this.instantiationService.invokeFunction(reviewNotebookEdits, uri, edits, tokenSource.token); + } + private tryToRevealCodeBlock(codeEditor: IActiveCodeEditor, codeBlock: string): void { const match = codeBlock.match(/(\S[^\n]*)\n/); // substring that starts with a non-whitespace character and ends with a newline if (match && match[1].length > 10) { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 194f4a239c0..7d68a1c937c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -60,6 +60,8 @@ import { observableConfigValue } from '../../../../platform/observable/common/pl import { ISharedWebContentExtractorService } from '../../../../platform/webContentExtractor/common/webContentExtractor.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { resolveImageEditorAttachContext } from '../../chat/browser/chatAttachmentResolve.js'; +import { INotebookService } from '../../notebook/common/notebookService.js'; +import { ICellEditOperation } from '../../notebook/common/notebookCommon.js'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -1529,6 +1531,64 @@ export async function reviewEdits(accessor: ServicesAccessor, editor: ICodeEdito chatModel.completeResponse(chatRequest); } + const isSettled = derived(r => { + const entry = editSession?.readEntry(uri, r); + if (!entry) { + return false; + } + const state = entry.state.read(r); + return state === ModifiedFileEntryState.Accepted || state === ModifiedFileEntryState.Rejected; + }); + const whenDecided = waitForState(isSettled, Boolean); + await raceCancellation(whenDecided, token); + store.dispose(); + return true; +} + +export async function reviewNotebookEdits(accessor: ServicesAccessor, uri: URI, stream: AsyncIterable<[URI, TextEdit[]] | ICellEditOperation[]>, token: CancellationToken): Promise { + + const chatService = accessor.get(IChatService); + const notebookService = accessor.get(INotebookService); + const isNotebook = notebookService.hasSupportedNotebooks(uri); + const chatModel = chatService.startSession(ChatAgentLocation.Editor, token, false); + + chatModel.startEditingSession(true); + + const editSession = await chatModel.editingSessionObs?.promise; + + const store = new DisposableStore(); + store.add(chatModel); + + // STREAM + const chatRequest = chatModel?.addRequest({ text: '', parts: [] }, { variables: [] }, 0); + assertType(chatRequest.response); + if (isNotebook) { + chatRequest.response.updateContent({ kind: 'notebookEdit', uri, edits: [], done: false }); + } else { + chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: false }); + } + for await (const chunk of stream) { + + if (token.isCancellationRequested) { + chatRequest.response.cancel(); + break; + } + if (chunk.every(isCellEditOperation)) { + chatRequest.response.updateContent({ kind: 'notebookEdit', uri, edits: chunk, done: false }); + } else { + chatRequest.response.updateContent({ kind: 'textEdit', uri: chunk[0], edits: chunk[1], done: false }); + } + } + if (isNotebook) { + chatRequest.response.updateContent({ kind: 'notebookEdit', uri, edits: [], done: true }); + } else { + chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: true }); + } + + if (!token.isCancellationRequested) { + chatRequest.response.complete(); + } + const isSettled = derived(r => { const entry = editSession?.readEntry(uri, r); if (!entry) { @@ -1547,6 +1607,15 @@ export async function reviewEdits(accessor: ServicesAccessor, editor: ICodeEdito return true; } +function isCellEditOperation(edit: URI | TextEdit[] | ICellEditOperation): edit is ICellEditOperation { + if (URI.isUri(edit)) { + return false; + } + if (Array.isArray(edit)) { + return false; + } + return true; +} async function moveToPanelChat(accessor: ServicesAccessor, model: ChatModel | undefined) { From 146b12cdd0c393c389e2f4c271a0ecb96b2a1dc5 Mon Sep 17 00:00:00 2001 From: Anthony Stewart <150152+a-stewart@users.noreply.github.com> Date: Wed, 7 May 2025 15:41:20 +0200 Subject: [PATCH 81/90] For editor font choice, if OS is not detected assume Linux (#248133) For editor font choice, if OS is not detected assume Linux for best compatibility chances --- src/vs/editor/common/config/editorOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 4beb40d2156..9e200454323 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -5425,7 +5425,7 @@ const DEFAULT_LINUX_FONT_FAMILY = '\'Droid Sans Mono\', \'monospace\', monospace */ export const EDITOR_FONT_DEFAULTS = { fontFamily: ( - platform.isMacintosh ? DEFAULT_MAC_FONT_FAMILY : (platform.isLinux ? DEFAULT_LINUX_FONT_FAMILY : DEFAULT_WINDOWS_FONT_FAMILY) + platform.isMacintosh ? DEFAULT_MAC_FONT_FAMILY : (platform.isWindows ? DEFAULT_WINDOWS_FONT_FAMILY : DEFAULT_LINUX_FONT_FAMILY) ), fontWeight: 'normal', fontSize: ( From cb0950e9d730fde4b6682cfdf0759750e4b54792 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 May 2025 15:52:46 +0200 Subject: [PATCH 82/90] Migrate github extension to ESM (2nd attempt) (#248312) * Revert "GitHub - revert ESM migration (#247322)" This reverts commit 2047ab0fff815f0ed5ceef0f370d8692ccfbbe6e. * use `"@vscode/extension-telemetry": "^1.0.0"` which doesn't use default export anymore --- build/lib/extensions.js | 7 +- build/lib/extensions.ts | 9 +- ...config.js => extension.webpack.config.cjs} | 10 + extensions/github/package-lock.json | 367 +++++++----------- extensions/github/package.json | 7 +- extensions/github/src/auth.ts | 4 +- extensions/github/src/branchProtection.ts | 8 +- extensions/github/src/canonicalUriProvider.ts | 2 +- extensions/github/src/commands.ts | 8 +- extensions/github/src/credentialProvider.ts | 4 +- extensions/github/src/extension.ts | 28 +- .../github/src/historyItemDetailsProvider.ts | 8 +- extensions/github/src/links.ts | 4 +- extensions/github/src/publish.ts | 6 +- extensions/github/src/pushErrorHandler.ts | 8 +- extensions/github/src/remoteSourceProvider.ts | 8 +- .../github/src/remoteSourcePublisher.ts | 4 +- extensions/github/src/shareProviders.ts | 6 +- extensions/github/src/test/github.test.ts | 2 +- extensions/github/src/test/index.ts | 4 +- extensions/github/src/util.ts | 2 +- extensions/github/tsconfig.json | 4 + 22 files changed, 227 insertions(+), 283 deletions(-) rename extensions/github/{extension.webpack.config.js => extension.webpack.config.cjs} (77%) diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 79a9507f66c..1d2f95299c8 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -61,7 +61,6 @@ const crypto_1 = __importDefault(require("crypto")); const vinyl_1 = __importDefault(require("vinyl")); const stats_1 = require("./stats"); const util2 = __importStar(require("./util")); -const vzip = require('gulp-vinyl-zip'); const gulp_filter_1 = __importDefault(require("gulp-filter")); const gulp_rename_1 = __importDefault(require("gulp-rename")); const fancy_log_1 = __importDefault(require("fancy-log")); @@ -72,6 +71,7 @@ const dependencies_1 = require("./dependencies"); const builtInExtensions_1 = require("./builtInExtensions"); const getVersion_1 = require("./getVersion"); const fetch_1 = require("./fetch"); +const vzip = require('gulp-vinyl-zip'); const root = path_1.default.dirname(path_1.default.dirname(__dirname)); const commit = (0, getVersion_1.getVersion)(root); const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`; @@ -104,7 +104,10 @@ function updateExtensionPackageJSON(input, update) { .pipe(packageJsonFilter.restore); } function fromLocal(extensionPath, forWeb, disableMangle) { - const webpackConfigFileName = forWeb ? 'extension-browser.webpack.config.js' : 'extension.webpack.config.js'; + const esm = JSON.parse(fs_1.default.readFileSync(path_1.default.join(extensionPath, 'package.json'), 'utf8')).type === 'module'; + const webpackConfigFileName = forWeb + ? `extension-browser.webpack.config.${!esm ? 'js' : 'cjs'}` + : `extension.webpack.config.${!esm ? 'js' : 'cjs'}`; const isWebPacked = fs_1.default.existsSync(path_1.default.join(extensionPath, webpackConfigFileName)); let input = isWebPacked ? fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle) diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 908480b6077..b900802ed6a 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -14,7 +14,6 @@ import { Stream } from 'stream'; import File from 'vinyl'; import { createStatsStream } from './stats'; import * as util2 from './util'; -const vzip = require('gulp-vinyl-zip'); import filter from 'gulp-filter'; import rename from 'gulp-rename'; import fancyLog from 'fancy-log'; @@ -26,6 +25,7 @@ import { getProductionDependencies } from './dependencies'; import { IExtensionDefinition, getExtensionStream } from './builtInExtensions'; import { getVersion } from './getVersion'; import { fetchUrls, fetchGithub } from './fetch'; +const vzip = require('gulp-vinyl-zip'); const root = path.dirname(path.dirname(__dirname)); const commit = getVersion(root); @@ -62,7 +62,12 @@ function updateExtensionPackageJSON(input: Stream, update: (data: any) => any): } function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolean): Stream { - const webpackConfigFileName = forWeb ? 'extension-browser.webpack.config.js' : 'extension.webpack.config.js'; + + const esm = JSON.parse(fs.readFileSync(path.join(extensionPath, 'package.json'), 'utf8')).type === 'module'; + + const webpackConfigFileName = forWeb + ? `extension-browser.webpack.config.${!esm ? 'js' : 'cjs'}` + : `extension.webpack.config.${!esm ? 'js' : 'cjs'}`; const isWebPacked = fs.existsSync(path.join(extensionPath, webpackConfigFileName)); let input = isWebPacked diff --git a/extensions/github/extension.webpack.config.js b/extensions/github/extension.webpack.config.cjs similarity index 77% rename from extensions/github/extension.webpack.config.js rename to extensions/github/extension.webpack.config.cjs index 45600607fc5..75b86c82b68 100644 --- a/extensions/github/extension.webpack.config.js +++ b/extensions/github/extension.webpack.config.cjs @@ -13,5 +13,15 @@ module.exports = withDefaults({ context: __dirname, entry: { extension: './src/extension.ts' + }, + output: { + libraryTarget: 'module', + chunkFormat: 'module', + }, + externals: { + 'vscode': 'module vscode', + }, + experiments: { + outputModule: true } }); diff --git a/extensions/github/package-lock.json b/extensions/github/package-lock.json index 1b7dc727a92..314527b3771 100644 --- a/extensions/github/package-lock.json +++ b/extensions/github/package-lock.json @@ -9,10 +9,10 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@octokit/graphql": "5.0.5", + "@octokit/graphql": "8.2.0", "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "19.0.4", - "@vscode/extension-telemetry": "^0.9.8", + "@octokit/rest": "21.1.0", + "@vscode/extension-telemetry": "^1.0.0", "tunnel": "^0.0.6" }, "devDependencies": { @@ -147,96 +147,92 @@ "license": "MIT" }, "node_modules/@octokit/auth-token": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", - "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", - "dependencies": { - "@octokit/types": "^7.0.0" - }, + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "node": ">= 18" } }, "node_modules/@octokit/core": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", - "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", + "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.2.2", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" + } + }, + "node_modules/@octokit/core/node_modules/@octokit/graphql": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", + "dependencies": { + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" } }, "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", + "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==" }, "node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", + "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/openapi-types": "^25.0.0" } }, "node_modules/@octokit/endpoint": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", - "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", "dependencies": { - "@octokit/types": "^7.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", + "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==" }, "node_modules/@octokit/endpoint/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", + "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/openapi-types": "^25.0.0" } }, "node_modules/@octokit/graphql": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", - "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.0.tgz", + "integrity": "sha512-gejfDywEml/45SqbWTWrhfwvLBrcGYhOn50sPOjIeVvH6i7D16/9xcFA8dAJNp2HMcd+g4vru41g4E2RBiZvfQ==", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/request": "^9.1.4", + "@octokit/types": "^13.8.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/graphql-schema": { @@ -249,148 +245,121 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.0.tgz", - "integrity": "sha512-rnI26BAITDZTo5vqFOmA7oX4xRd18rO+gcK4MiTpJmsRMxAw0JmevNjPsjpry1bb9SVNo56P/0kbiyXXa4QluA==" + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.2.0.tgz", - "integrity": "sha512-8otLCIK9esfmOCY14CBnG/xPqv0paf14rc+s9tHpbOpeFwrv5CnECKW1qdqMAT60ngAa9eB1bKQ+l2YCpi0HPQ==", + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.6.0.tgz", + "integrity": "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==", "dependencies": { - "@octokit/types": "^7.2.0" + "@octokit/types": "^13.10.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=4" - } - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "engines": { + "node": ">= 18" + }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.4.0.tgz", - "integrity": "sha512-YP4eUqZ6vORy/eZOTdil1ZSrMt0kv7i/CVw+HhC2C0yJN+IqTc/rot957JQ7JfyeJD6HZOjLg6Jp1o9cPhI9KA==", + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.5.0.tgz", + "integrity": "sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw==", "dependencies": { - "@octokit/types": "^7.2.0", - "deprecation": "^2.3.1" + "@octokit/types": "^13.10.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/core": ">=6" } }, "node_modules/@octokit/request": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", - "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", + "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/request-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", - "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", "dependencies": { - "@octokit/types": "^7.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^14.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", + "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==" }, "node_modules/@octokit/request-error/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", + "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/openapi-types": "^25.0.0" } }, "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", + "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==" }, "node_modules/@octokit/request/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", + "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/openapi-types": "^25.0.0" } }, "node_modules/@octokit/rest": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", - "integrity": "sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA==", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.0.tgz", + "integrity": "sha512-93iLxcKDJboUpmnUyeJ6cRIi7z7cqTZT1K7kRK4LobGxwTwpsa+2tQQbRQNGy7IFDEAmrtkf4F4wBj3D5rVlJQ==", "dependencies": { - "@octokit/core": "^4.0.0", - "@octokit/plugin-paginate-rest": "^4.0.0", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + "@octokit/core": "^6.1.3", + "@octokit/plugin-paginate-rest": "^11.4.0", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.3.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/types": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.0.tgz", - "integrity": "sha512-xySzJG4noWrIBFyMu4lg4tu9vAgNg9S0aoLRONhAEz6ueyi1evBzb40HitIosaYS4XOexphG305IVcLrIX/30g==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "dependencies": { - "@octokit/openapi-types": "^17.1.0" + "@octokit/openapi-types": "^24.2.0" } }, "node_modules/@types/node": { @@ -403,10 +372,9 @@ } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", - "license": "MIT", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.0.0.tgz", + "integrity": "sha512-vaTZE65zigWwSWYB6yaZUAyVC/Ux+6U82hnzy/ejuS/KpFifO+0oORNd5yAoPeIRnYjvllM6ES3YlX4K5tUuww==", "dependencies": { "@microsoft/1ds-core-js": "^4.3.4", "@microsoft/1ds-post-js": "^4.3.4", @@ -417,14 +385,24 @@ } }, "node_modules/before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==" }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, "node_modules/graphql": { "version": "16.8.1", @@ -448,46 +426,6 @@ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", @@ -508,28 +446,9 @@ "dev": true }, "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" } } } diff --git a/extensions/github/package.json b/extensions/github/package.json index 524cee5bbea..75cd0827bcb 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -20,6 +20,7 @@ "vscode.git-base" ], "main": "./out/extension.js", + "type": "module", "capabilities": { "virtualWorkspaces": false, "untrustedWorkspaces": { @@ -227,11 +228,11 @@ "watch": "gulp watch-extension:github" }, "dependencies": { - "@octokit/graphql": "5.0.5", + "@octokit/graphql": "8.2.0", "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "19.0.4", + "@octokit/rest": "21.1.0", "tunnel": "^0.0.6", - "@vscode/extension-telemetry": "^0.9.8" + "@vscode/extension-telemetry": "^1.0.0" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/github/src/auth.ts b/extensions/github/src/auth.ts index e7be2637da0..f27967d22f7 100644 --- a/extensions/github/src/auth.ts +++ b/extensions/github/src/auth.ts @@ -5,7 +5,7 @@ import { AuthenticationSession, authentication, window } from 'vscode'; import { Agent, globalAgent } from 'https'; -import { graphql } from '@octokit/graphql/dist-types/types'; +import { graphql } from '@octokit/graphql/types'; import { Octokit } from '@octokit/rest'; import { httpsOverHttp } from 'tunnel'; import { URL } from 'url'; @@ -71,7 +71,7 @@ export async function getOctokitGraphql(): Promise { const token = session.accessToken; const { graphql } = await import('@octokit/graphql'); - return graphql.defaults({ + return graphql({ headers: { authorization: `token ${token}` }, diff --git a/extensions/github/src/branchProtection.ts b/extensions/github/src/branchProtection.ts index 52b4fbfae22..7b56f5aab40 100644 --- a/extensions/github/src/branchProtection.ts +++ b/extensions/github/src/branchProtection.ts @@ -5,10 +5,10 @@ import { authentication, EventEmitter, LogOutputChannel, Memento, Uri, workspace } from 'vscode'; import { Repository as GitHubRepository, RepositoryRuleset } from '@octokit/graphql-schema'; -import { AuthenticationError, getOctokitGraphql } from './auth'; -import { API, BranchProtection, BranchProtectionProvider, BranchProtectionRule, Repository } from './typings/git'; -import { DisposableStore, getRepositoryFromUrl } from './util'; -import TelemetryReporter from '@vscode/extension-telemetry'; +import { AuthenticationError, getOctokitGraphql } from './auth.js'; +import { API, BranchProtection, BranchProtectionProvider, BranchProtectionRule, Repository } from './typings/git.js'; +import { DisposableStore, getRepositoryFromUrl } from './util.js'; +import { TelemetryReporter } from '@vscode/extension-telemetry'; const REPOSITORY_QUERY = ` query repositoryPermissions($owner: String!, $repo: String!) { diff --git a/extensions/github/src/canonicalUriProvider.ts b/extensions/github/src/canonicalUriProvider.ts index 09f5e243bc1..0838c7377dd 100644 --- a/extensions/github/src/canonicalUriProvider.ts +++ b/extensions/github/src/canonicalUriProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken, CanonicalUriProvider, CanonicalUriRequestOptions, Disposable, ProviderResult, Uri, workspace } from 'vscode'; -import { API } from './typings/git'; +import { API } from './typings/git.js'; const SUPPORTED_SCHEMES = ['ssh', 'https', 'file']; diff --git a/extensions/github/src/commands.ts b/extensions/github/src/commands.ts index 4e5587c09b5..48e9574c708 100644 --- a/extensions/github/src/commands.ts +++ b/extensions/github/src/commands.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { API as GitAPI, RefType, Repository } from './typings/git'; -import { publishRepository } from './publish'; -import { DisposableStore, getRepositoryFromUrl } from './util'; -import { LinkContext, getCommitLink, getLink, getVscodeDevHost } from './links'; +import { API as GitAPI, RefType, Repository } from './typings/git.js'; +import { publishRepository } from './publish.js'; +import { DisposableStore, getRepositoryFromUrl } from './util.js'; +import { LinkContext, getCommitLink, getLink, getVscodeDevHost } from './links.js'; async function copyVscodeDevLink(gitAPI: GitAPI, useSelection: boolean, context: LinkContext, includeRange = true) { try { diff --git a/extensions/github/src/credentialProvider.ts b/extensions/github/src/credentialProvider.ts index 14c7e6a2c73..d184960c23b 100644 --- a/extensions/github/src/credentialProvider.ts +++ b/extensions/github/src/credentialProvider.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CredentialsProvider, Credentials, API as GitAPI } from './typings/git'; +import { CredentialsProvider, Credentials, API as GitAPI } from './typings/git.js'; import { workspace, Uri, Disposable } from 'vscode'; -import { getSession } from './auth'; +import { getSession } from './auth.js'; const EmptyDisposable: Disposable = { dispose() { } }; diff --git a/extensions/github/src/extension.ts b/extensions/github/src/extension.ts index de0349ba9d3..bf07a0b9278 100644 --- a/extensions/github/src/extension.ts +++ b/extensions/github/src/extension.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { commands, Disposable, ExtensionContext, extensions, l10n, LogLevel, LogOutputChannel, window } from 'vscode'; -import TelemetryReporter from '@vscode/extension-telemetry'; -import { GithubRemoteSourceProvider } from './remoteSourceProvider'; -import { API, GitExtension } from './typings/git'; -import { registerCommands } from './commands'; -import { GithubCredentialProviderManager } from './credentialProvider'; -import { DisposableStore, repositoryHasGitHubRemote } from './util'; -import { GithubPushErrorHandler } from './pushErrorHandler'; -import { GitBaseExtension } from './typings/git-base'; -import { GithubRemoteSourcePublisher } from './remoteSourcePublisher'; -import { GitHubBranchProtectionProviderManager } from './branchProtection'; -import { GitHubCanonicalUriProvider } from './canonicalUriProvider'; -import { VscodeDevShareProvider } from './shareProviders'; -import { GitHubSourceControlHistoryItemDetailsProvider } from './historyItemDetailsProvider'; +import { TelemetryReporter } from '@vscode/extension-telemetry'; +import { GithubRemoteSourceProvider } from './remoteSourceProvider.js'; +import { API, GitExtension } from './typings/git.js'; +import { registerCommands } from './commands.js'; +import { GithubCredentialProviderManager } from './credentialProvider.js'; +import { DisposableStore, repositoryHasGitHubRemote } from './util.js'; +import { GithubPushErrorHandler } from './pushErrorHandler.js'; +import { GitBaseExtension } from './typings/git-base.js'; +import { GithubRemoteSourcePublisher } from './remoteSourcePublisher.js'; +import { GitHubBranchProtectionProviderManager } from './branchProtection.js'; +import { GitHubCanonicalUriProvider } from './canonicalUriProvider.js'; +import { VscodeDevShareProvider } from './shareProviders.js'; +import { GitHubSourceControlHistoryItemDetailsProvider } from './historyItemDetailsProvider.js'; export function activate(context: ExtensionContext): void { const disposables: Disposable[] = []; @@ -31,7 +31,7 @@ export function activate(context: ExtensionContext): void { disposables.push(logger.onDidChangeLogLevel(onDidChangeLogLevel)); onDidChangeLogLevel(logger.logLevel); - const { aiKey } = require('../package.json') as { aiKey: string }; + const { aiKey } = context.extension.packageJSON as { aiKey: string }; const telemetryReporter = new TelemetryReporter(aiKey); disposables.push(telemetryReporter); diff --git a/extensions/github/src/historyItemDetailsProvider.ts b/extensions/github/src/historyItemDetailsProvider.ts index b31126e2ada..add55e6f403 100644 --- a/extensions/github/src/historyItemDetailsProvider.ts +++ b/extensions/github/src/historyItemDetailsProvider.ts @@ -5,10 +5,10 @@ import { authentication, Command, l10n, LogOutputChannel, workspace } from 'vscode'; import { Commit, Repository as GitHubRepository, Maybe } from '@octokit/graphql-schema'; -import { API, AvatarQuery, AvatarQueryCommit, Repository, SourceControlHistoryItemDetailsProvider } from './typings/git'; -import { DisposableStore, getRepositoryDefaultRemote, getRepositoryDefaultRemoteUrl, getRepositoryFromUrl, groupBy, sequentialize } from './util'; -import { AuthenticationError, getOctokitGraphql } from './auth'; -import { getAvatarLink } from './links'; +import { API, AvatarQuery, AvatarQueryCommit, Repository, SourceControlHistoryItemDetailsProvider } from './typings/git.js'; +import { DisposableStore, getRepositoryDefaultRemote, getRepositoryDefaultRemoteUrl, getRepositoryFromUrl, groupBy, sequentialize } from './util.js'; +import { AuthenticationError, getOctokitGraphql } from './auth.js'; +import { getAvatarLink } from './links.js'; const ISSUE_EXPRESSION = /(([A-Za-z0-9_.\-]+)\/([A-Za-z0-9_.\-]+))?(#|GH-)([1-9][0-9]*)($|\b)/g; diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts index fdcac0c5cfd..8eb0f6b23f6 100644 --- a/extensions/github/src/links.ts +++ b/extensions/github/src/links.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { API as GitAPI, RefType, Repository } from './typings/git'; -import { getRepositoryFromUrl, repositoryHasGitHubRemote } from './util'; +import { API as GitAPI, RefType, Repository } from './typings/git.js'; +import { getRepositoryFromUrl, repositoryHasGitHubRemote } from './util.js'; export function isFileInRepo(repository: Repository, file: vscode.Uri): boolean { return file.path.toLowerCase() === repository.rootUri.path.toLowerCase() || diff --git a/extensions/github/src/publish.ts b/extensions/github/src/publish.ts index dee8898d348..587b2652d61 100644 --- a/extensions/github/src/publish.ts +++ b/extensions/github/src/publish.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { API as GitAPI, Repository } from './typings/git'; -import { getOctokit } from './auth'; +import { API as GitAPI, Repository } from './typings/git.js'; +import { getOctokit } from './auth.js'; import { TextEncoder } from 'util'; import { basename } from 'path'; import { Octokit } from '@octokit/rest'; -import { isInCodespaces } from './pushErrorHandler'; +import { isInCodespaces } from './pushErrorHandler.js'; function sanitizeRepositoryName(value: string): string { return value.trim().replace(/[^a-z0-9_.]/ig, '-'); diff --git a/extensions/github/src/pushErrorHandler.ts b/extensions/github/src/pushErrorHandler.ts index f1702bf15dd..f7b0b9ef869 100644 --- a/extensions/github/src/pushErrorHandler.ts +++ b/extensions/github/src/pushErrorHandler.ts @@ -5,10 +5,12 @@ import { TextDecoder } from 'util'; import { commands, env, ProgressLocation, Uri, window, workspace, QuickPickOptions, FileType, l10n, Disposable, TextDocumentContentProvider } from 'vscode'; -import TelemetryReporter from '@vscode/extension-telemetry'; -import { getOctokit } from './auth'; -import { GitErrorCodes, PushErrorHandler, Remote, Repository } from './typings/git'; +import { getOctokit } from './auth.js'; +import { GitErrorCodes, PushErrorHandler, Remote, Repository } from './typings/git.js'; import * as path from 'path'; +import { TelemetryReporter } from '@vscode/extension-telemetry'; + + type Awaited = T extends PromiseLike ? Awaited : T; diff --git a/extensions/github/src/remoteSourceProvider.ts b/extensions/github/src/remoteSourceProvider.ts index 0d8b9340695..291a3f1a6ba 100644 --- a/extensions/github/src/remoteSourceProvider.ts +++ b/extensions/github/src/remoteSourceProvider.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Uri, env, l10n, workspace } from 'vscode'; -import { RemoteSourceProvider, RemoteSource, RemoteSourceAction } from './typings/git-base'; -import { getOctokit } from './auth'; +import { RemoteSourceProvider, RemoteSource, RemoteSourceAction } from './typings/git-base.js'; +import { getOctokit } from './auth.js'; import { Octokit } from '@octokit/rest'; -import { getRepositoryFromQuery, getRepositoryFromUrl } from './util'; -import { getBranchLink, getVscodeDevHost } from './links'; +import { getRepositoryFromQuery, getRepositoryFromUrl } from './util.js'; +import { getBranchLink, getVscodeDevHost } from './links.js'; function asRemoteSource(raw: any): RemoteSource { const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol'); diff --git a/extensions/github/src/remoteSourcePublisher.ts b/extensions/github/src/remoteSourcePublisher.ts index 2e6a5d88ead..97ce05a835c 100644 --- a/extensions/github/src/remoteSourcePublisher.ts +++ b/extensions/github/src/remoteSourcePublisher.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { publishRepository } from './publish'; -import { API as GitAPI, RemoteSourcePublisher, Repository } from './typings/git'; +import { publishRepository } from './publish.js'; +import { API as GitAPI, RemoteSourcePublisher, Repository } from './typings/git.js'; export class GithubRemoteSourcePublisher implements RemoteSourcePublisher { readonly name = 'GitHub'; diff --git a/extensions/github/src/shareProviders.ts b/extensions/github/src/shareProviders.ts index 7aea9c27b24..d2e94a47147 100644 --- a/extensions/github/src/shareProviders.ts +++ b/extensions/github/src/shareProviders.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { API } from './typings/git'; -import { getRepositoryFromUrl, repositoryHasGitHubRemote } from './util'; -import { encodeURIComponentExceptSlashes, ensurePublished, getRepositoryForFile, notebookCellRangeString, rangeString } from './links'; +import { API } from './typings/git.js'; +import { getRepositoryFromUrl, repositoryHasGitHubRemote } from './util.js'; +import { encodeURIComponentExceptSlashes, ensurePublished, getRepositoryForFile, notebookCellRangeString, rangeString } from './links.js'; export class VscodeDevShareProvider implements vscode.ShareProvider, vscode.Disposable { readonly id: string = 'copyVscodeDevLink'; diff --git a/extensions/github/src/test/github.test.ts b/extensions/github/src/test/github.test.ts index 2fc5fbd23a5..db0eba515cb 100644 --- a/extensions/github/src/test/github.test.ts +++ b/extensions/github/src/test/github.test.ts @@ -6,7 +6,7 @@ import 'mocha'; import * as assert from 'assert'; import { workspace, extensions, Uri, commands } from 'vscode'; -import { findPullRequestTemplates, pickPullRequestTemplate } from '../pushErrorHandler'; +import { findPullRequestTemplates, pickPullRequestTemplate } from '../pushErrorHandler.js'; suite('github smoke test', function () { const cwd = workspace.workspaceFolders![0].uri; diff --git a/extensions/github/src/test/index.ts b/extensions/github/src/test/index.ts index 52c5acf885f..6573ab1daa4 100644 --- a/extensions/github/src/test/index.ts +++ b/extensions/github/src/test/index.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import * as testRunner from '../../../../test/integration/electron/testrunner'; +import * as testRunner from '../../../../test/integration/electron/testrunner.js'; const suite = 'Github Tests'; @@ -27,4 +27,4 @@ if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { testRunner.configure(options); -export = testRunner; +export default testRunner; diff --git a/extensions/github/src/util.ts b/extensions/github/src/util.ts index 4c8a032405d..1841ba0d032 100644 --- a/extensions/github/src/util.ts +++ b/extensions/github/src/util.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Repository } from './typings/git'; +import { Repository } from './typings/git.js'; export class DisposableStore { diff --git a/extensions/github/tsconfig.json b/extensions/github/tsconfig.json index 8435c0d09e8..f8459d0f629 100644 --- a/extensions/github/tsconfig.json +++ b/extensions/github/tsconfig.json @@ -1,8 +1,12 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", "outDir": "./out", + "skipLibCheck": true, "experimentalDecorators": true, + "allowSyntheticDefaultImports": false, "typeRoots": [ "./node_modules/@types" ] From d790d95d8d2bc728743799698346ddb34fd71a4a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 May 2025 16:10:03 +0200 Subject: [PATCH 83/90] chore - more type-strict `isUri(...)`-function (#248315) --- src/vs/base/common/uri.ts | 4 ++-- src/vs/base/test/common/uri.test.ts | 6 ++++++ src/vs/monaco.d.ts | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 73a3aa6cd49..f04f368a27d 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -97,11 +97,11 @@ const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; */ export class URI implements UriComponents { - static isUri(thing: any): thing is URI { + static isUri(thing: unknown): thing is URI { if (thing instanceof URI) { return true; } - if (!thing) { + if (!thing || typeof thing !== 'object') { return false; } return typeof (thing).authority === 'string' diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 8e4239460d1..323789a764e 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -470,6 +470,12 @@ suite('URI', () => { with() { return this; }, toString() { return ''; } }), true); + + assert.strictEqual(URI.isUri(1), false); + assert.strictEqual(URI.isUri("1"), false); + assert.strictEqual(URI.isUri("http://sample.com"), false); + assert.strictEqual(URI.isUri(null), false); + assert.strictEqual(URI.isUri(undefined), false); }); test('isUriComponents', function () { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index a5ce6e74f66..0116beab8b8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -125,7 +125,7 @@ declare namespace monaco { * ``` */ export class Uri implements UriComponents { - static isUri(thing: any): thing is Uri; + static isUri(thing: unknown): thing is Uri; /** * scheme is the 'http' part of 'http://www.example.com/some/path?query#fragment'. * The part before the first colon. From 4e38f25dd730a56d060f52ff4148ebf456354a50 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 7 May 2025 14:23:59 +0000 Subject: [PATCH 84/90] SCM - don't render the overview rules in the quick diff widget (#248316) --- src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index 9df41d9090e..6d763793ebc 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -380,10 +380,10 @@ class QuickDiffWidget extends PeekViewWidget { fixedOverflowWidgets: true, ignoreTrimWhitespace: false, minimap: { enabled: false }, - overviewRulerLanes: 2, readOnly: false, renderGutterMenu: false, renderIndicators: false, + renderOverviewRuler: false, renderSideBySide: false, scrollbar: { verticalScrollbarSize: 14, From 97e317f161d8aa14057ad348bbc603a6823e4bf1 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 16:22:27 +0200 Subject: [PATCH 85/90] Fixes https://github.com/microsoft/vscode/issues/246782 --- ...CompletionLanguageStatusBarContribution.ts | 58 +++++++++---------- .../browser/inlineCompletions.contribution.ts | 4 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts index 586e2251675..8177c0b0e52 100644 --- a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts +++ b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts @@ -5,41 +5,50 @@ import { createHotClass } from '../../../../base/common/hotReloadHelpers.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { autorunWithStore, debouncedObservable, derived } from '../../../../base/common/observable.js'; +import { autorunWithStore, debouncedObservable, derived, observableFromEvent } from '../../../../base/common/observable.js'; import Severity from '../../../../base/common/severity.js'; -import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; +import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js'; import { localize } from '../../../../nls.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ILanguageStatusService } from '../../../services/languageStatus/common/languageStatusService.js'; -export class InlineCompletionLanguageStatusBarContribution extends Disposable { +export class InlineCompletionLanguageStatusBarContribution extends Disposable implements IWorkbenchContribution { public static readonly hot = createHotClass(InlineCompletionLanguageStatusBarContribution); - public static Id = 'vs.editor.contrib.inlineCompletionLanguageStatusBarContribution'; + public static Id = 'vs.contrib.inlineCompletionLanguageStatusBarContribution'; public static readonly languageStatusBarDisposables = new Set(); - private readonly _c = InlineCompletionsController.get(this._editor); - - private readonly _state = derived(this, reader => { - const model = this._c?.model.read(reader); - if (!model) { return undefined; } - if (!observableCodeEditor(this._editor).isFocused.read(reader)) { - return undefined; - } - - return { - model, - status: debouncedObservable(model.status, 300), - }; - }); + private _activeEditor; + private _state; constructor( - private readonly _editor: ICodeEditor, @ILanguageStatusService private readonly _languageStatusService: ILanguageStatusService, + @IEditorService private readonly _editorService: IEditorService, ) { super(); + + this._activeEditor = observableFromEvent(this, _editorService.onDidActiveEditorChange, () => this._editorService.activeTextEditorControl); + this._state = derived(this, reader => { + const editor = this._activeEditor.read(reader); + if (!editor || !isCodeEditor(editor)) { + return undefined; + } + + const c = InlineCompletionsController.get(editor); + const model = c?.model.read(reader); + if (!model) { + return undefined; + } + + return { + model, + status: debouncedObservable(model.status, 300), + }; + }); + this._register(autorunWithStore((reader, store) => { const state = this._state.read(reader); if (!state) { @@ -55,15 +64,6 @@ export class InlineCompletionLanguageStatusBarContribution extends Disposable { noSuggestion: { shortLabel: '$(circle-slash)', label: '$(copilot) ' + localize('noInlineSuggestionAvailable', "No inline suggestion available"), loading: false, }, }; - // Make sure previous status is cleared before the new is registered. This works, but is a bit hacky. - // TODO: Use a workbench contribution to get singleton behavior. - InlineCompletionLanguageStatusBarContribution.languageStatusBarDisposables.forEach(d => d.clear()); - - InlineCompletionLanguageStatusBarContribution.languageStatusBarDisposables.add(store); - store.add({ - dispose: () => InlineCompletionLanguageStatusBarContribution.languageStatusBarDisposables.delete(store) - }); - store.add(this._languageStatusService.addStatus({ accessibilityInfo: undefined, busy: statusMap[status].loading, diff --git a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts index f4c52cc54b0..d7cd1537bbb 100644 --- a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts +++ b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js'; import { wrapInHotClass1 } from '../../../../platform/observable/common/wrapInHotClass.js'; +import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; import { InlineCompletionLanguageStatusBarContribution } from './inlineCompletionLanguageStatusBarContribution.js'; -registerEditorContribution(InlineCompletionLanguageStatusBarContribution.Id, wrapInHotClass1(InlineCompletionLanguageStatusBarContribution.hot), EditorContributionInstantiation.Eventually); +registerWorkbenchContribution2(InlineCompletionLanguageStatusBarContribution.Id, wrapInHotClass1(InlineCompletionLanguageStatusBarContribution.hot), WorkbenchPhase.Eventually); From 7e4d3b3f6376cf9dc3b6e62e0687e7913b04116f Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Wed, 7 May 2025 08:18:31 -0700 Subject: [PATCH 86/90] Update media (#248321) --- .../common/gettingStartedContent.ts | 2 +- .../common/media/ai-powered-suggestions.svg | 194 ++++++++--- .../common/media/customize-ai.svg | 164 +++++++++ .../common/media/multi-file-edits.svg | 326 +++++++++++------- 4 files changed, 521 insertions(+), 165 deletions(-) create mode 100644 src/vs/workbench/contrib/welcomeGettingStarted/common/media/customize-ai.svg diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 044c5c46a5c..59b7d5e9478 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -702,7 +702,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ title: localize('gettingStarted.customize.title', "Personalized to how you work"), description: localize('gettingStarted.customize.description', "Swap models, add agent mode tools, and create personalized instructions.\n{0}", Button(localize('signUp', "Set up AI"), 'command:workbench.action.chat.triggerSetupWithoutDialog')), media: { - type: 'svg', altText: 'Personalize', path: 'multi-file-edits.svg' + type: 'svg', altText: 'Personalize', path: 'customize-ai.svg' }, }, { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/ai-powered-suggestions.svg b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/ai-powered-suggestions.svg index c7e582b4d15..bcb0653de01 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/ai-powered-suggestions.svg +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/ai-powered-suggestions.svg @@ -1,63 +1,165 @@ - + - - - - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + - - - - - - + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - \ No newline at end of file + + + + + + + + + + + + diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/customize-ai.svg b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/customize-ai.svg new file mode 100644 index 00000000000..3594f6f9c6b --- /dev/null +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/customize-ai.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/multi-file-edits.svg b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/multi-file-edits.svg index 8dee86eda63..583a2c3607d 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/media/multi-file-edits.svg +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/multi-file-edits.svg @@ -1,121 +1,215 @@ - + - + - + - + - + - - + + - + - + - + - - + + - - + + - - + + - + - + - + - + - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - + + - + - + - + - + @@ -126,99 +220,98 @@ - + - + - - + - + - - - - - - - - - - - - + + + + + + + + + + + + - + - + - + - - - + + + - - - + + + - + - + - + - + - + - - - + + + - + - + - + - + - - + + @@ -229,14 +322,14 @@ - + - + - + @@ -260,12 +353,12 @@ - + - + @@ -312,66 +405,63 @@ - + - - + + - + - - + + - - + + - + - + - + - + - - + + - - - - + - + - + - + - - + + - - + + - + - + From 1c1e570a4c01e57b8b5cacfd0ece09d06ca520c3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 17:19:38 +0200 Subject: [PATCH 87/90] Moves edit datastructures into editor/common/core/edits (#248313) --- .../diffEditor/features/gutterFeature.ts | 2 +- .../common/core/{ => edits}/lineEdit.ts | 13 +++++------ .../common/core/{ => edits}/offsetEdit.ts | 4 ++-- .../common/core/{ => edits}/textEdit.ts | 23 ++++++++++--------- src/vs/editor/common/core/positionToOffset.ts | 4 ++-- .../defaultLinesDiffComputer.ts | 2 +- src/vs/editor/common/diff/rangeMapping.ts | 2 +- .../common/model/textModelOffsetEdit.ts | 2 +- src/vs/editor/common/model/textModelText.ts | 2 +- .../inlineCompletionsAccessibleView.ts | 4 ++-- .../browser/model/computeGhostText.ts | 2 +- .../browser/model/ghostText.ts | 2 +- .../browser/model/inlineCompletionsModel.ts | 2 +- .../browser/model/inlineCompletionsSource.ts | 2 +- .../browser/model/inlineEdit.ts | 2 +- .../browser/model/inlineSuggestionItem.ts | 4 ++-- .../browser/model/provideInlineCompletions.ts | 4 ++-- .../browser/model/singleTextEditHelpers.ts | 2 +- .../browser/model/suggestWidgetAdapter.ts | 2 +- .../inlineCompletions/browser/utils.ts | 2 +- .../browser/view/ghostText/ghostTextView.ts | 2 +- .../view/inlineEdits/inlineEditWithChanges.ts | 4 ++-- .../view/inlineEdits/inlineEditsModel.ts | 2 +- .../view/inlineEdits/inlineEditsView.ts | 2 +- .../inlineEdits/inlineEditsViewProducer.ts | 2 +- .../inlineEditsWordInsertView.ts | 2 +- .../inlineEditsWordReplacementView.ts | 4 ++-- .../originalEditorInlineDiffView.ts | 2 +- .../browser/view/inlineEdits/utils/utils.ts | 2 +- .../test/browser/computeGhostText.test.ts | 2 +- .../test/browser/getSecondaryEdits.test.ts | 2 +- .../inlineCompletions/test/browser/utils.ts | 2 +- src/vs/editor/test/common/core/random.ts | 4 ++-- .../editor/test/common/core/textEdit.test.ts | 2 +- .../combineTextEditInfos.test.ts | 2 +- .../diffing/defaultLinesDiffComputer.test.ts | 2 +- .../editor/test/node/diffing/fixtures.test.ts | 2 +- .../chatEditingModifiedDocumentEntry.ts | 2 +- .../chatEditingModifiedFileEntry.ts | 2 +- .../chatEditingModifiedNotebookEntry.ts | 2 +- .../chatEditing/chatEditingSessionStorage.ts | 2 +- .../notebook/chatEditingNotebookCellEntry.ts | 2 +- .../browser/chatEditingSessionStorage.test.ts | 2 +- 43 files changed, 67 insertions(+), 67 deletions(-) rename src/vs/editor/common/core/{ => edits}/lineEdit.ts (97%) rename src/vs/editor/common/core/{ => edits}/offsetEdit.ts (99%) rename src/vs/editor/common/core/{ => edits}/textEdit.ts (95%) diff --git a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts index 1187928002c..50f2c512102 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts @@ -20,7 +20,7 @@ import { EditorOption } from '../../../../common/config/editorOptions.js'; import { LineRange, LineRangeSet } from '../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Range } from '../../../../common/core/range.js'; -import { TextEdit } from '../../../../common/core/textEdit.js'; +import { TextEdit } from '../../../../common/core/edits/textEdit.js'; import { DetailedLineRangeMapping } from '../../../../common/diff/rangeMapping.js'; import { TextModelText } from '../../../../common/model/textModelText.js'; import { ActionRunnerWithContext } from '../../multiDiffEditor/utils.js'; diff --git a/src/vs/editor/common/core/lineEdit.ts b/src/vs/editor/common/core/edits/lineEdit.ts similarity index 97% rename from src/vs/editor/common/core/lineEdit.ts rename to src/vs/editor/common/core/edits/lineEdit.ts index d05709f2b79..c2018a1f106 100644 --- a/src/vs/editor/common/core/lineEdit.ts +++ b/src/vs/editor/common/core/edits/lineEdit.ts @@ -3,16 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareBy, groupAdjacentBy, numberComparator } from '../../../base/common/arrays.js'; -import { assert, checkAdjacentItems } from '../../../base/common/assert.js'; -import { splitLines } from '../../../base/common/strings.js'; -import { LineRange } from './ranges/lineRange.js'; +import { compareBy, groupAdjacentBy, numberComparator } from '../../../../base/common/arrays.js'; +import { assert, checkAdjacentItems } from '../../../../base/common/assert.js'; +import { splitLines } from '../../../../base/common/strings.js'; +import { LineRange } from '../ranges/lineRange.js'; import { OffsetEdit, SingleOffsetEdit } from './offsetEdit.js'; -import { Position } from './position.js'; -import { Range } from './range.js'; +import { Position } from '../position.js'; +import { Range } from '../range.js'; import { AbstractText, SingleTextEdit, TextEdit } from './textEdit.js'; - export class LineEdit { public static readonly empty = new LineEdit([]); diff --git a/src/vs/editor/common/core/offsetEdit.ts b/src/vs/editor/common/core/edits/offsetEdit.ts similarity index 99% rename from src/vs/editor/common/core/offsetEdit.ts rename to src/vs/editor/common/core/edits/offsetEdit.ts index 091dadb84e8..eb0b4014eca 100644 --- a/src/vs/editor/common/core/offsetEdit.ts +++ b/src/vs/editor/common/core/edits/offsetEdit.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { OffsetRange } from './offsetRange.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetRange } from '../offsetRange.js'; /** * Describes an edit to a (0-based) string. diff --git a/src/vs/editor/common/core/textEdit.ts b/src/vs/editor/common/core/edits/textEdit.ts similarity index 95% rename from src/vs/editor/common/core/textEdit.ts rename to src/vs/editor/common/core/edits/textEdit.ts index 9b11b2f95a1..278a7f80b19 100644 --- a/src/vs/editor/common/core/textEdit.ts +++ b/src/vs/editor/common/core/edits/textEdit.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { equals } from '../../../base/common/arrays.js'; -import { assert, assertFn, checkAdjacentItems } from '../../../base/common/assert.js'; -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../base/common/strings.js'; -import { ISingleEditOperation } from './editOperation.js'; -import { LineRange } from './ranges/lineRange.js'; +import { equals } from '../../../../base/common/arrays.js'; +import { assert, assertFn, checkAdjacentItems } from '../../../../base/common/assert.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../../base/common/strings.js'; +import { ISingleEditOperation } from '../editOperation.js'; +import { LineRange } from '../ranges/lineRange.js'; import { OffsetEdit } from './offsetEdit.js'; -import { Position } from './position.js'; -import { PositionOffsetTransformer } from './positionToOffset.js'; -import { Range } from './range.js'; -import { TextLength } from './textLength.js'; +import { Position } from '../position.js'; +import { PositionOffsetTransformer } from '../positionToOffset.js'; +import { Range } from '../range.js'; +import { TextLength } from '../textLength.js'; export class TextEdit { public static fromOffsetEdit(edit: OffsetEdit, initialState: AbstractText): TextEdit { @@ -446,10 +446,11 @@ export class ArrayText extends LineBasedText { } export class StringText extends AbstractText { - private readonly _t = new PositionOffsetTransformer(this.value); + private readonly _t; constructor(public readonly value: string) { super(); + this._t = new PositionOffsetTransformer(this.value); } getValueOfRange(range: Range): string { diff --git a/src/vs/editor/common/core/positionToOffset.ts b/src/vs/editor/common/core/positionToOffset.ts index dd8d81a2cbe..96fd066ef29 100644 --- a/src/vs/editor/common/core/positionToOffset.ts +++ b/src/vs/editor/common/core/positionToOffset.ts @@ -5,11 +5,11 @@ import { findLastIdxMonotonous } from '../../../base/common/arraysFind.js'; import { ITextModel } from '../model.js'; -import { OffsetEdit, SingleOffsetEdit } from './offsetEdit.js'; +import { OffsetEdit, SingleOffsetEdit } from './edits/offsetEdit.js'; import { OffsetRange } from './offsetRange.js'; import { Position } from './position.js'; import { Range } from './range.js'; -import { SingleTextEdit, TextEdit } from './textEdit.js'; +import { SingleTextEdit, TextEdit } from './edits/textEdit.js'; import { TextLength } from './textLength.js'; export abstract class PositionOffsetTransformerBase { diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts index 9b35229cb85..66f7b97442a 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts @@ -9,7 +9,7 @@ import { LineRange } from '../../core/ranges/lineRange.js'; import { OffsetRange } from '../../core/offsetRange.js'; import { Position } from '../../core/position.js'; import { Range } from '../../core/range.js'; -import { ArrayText } from '../../core/textEdit.js'; +import { ArrayText } from '../../core/edits/textEdit.js'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from '../linesDiffComputer.js'; import { DetailedLineRangeMapping, LineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../rangeMapping.js'; import { DateTimeout, InfiniteTimeout, ITimeout, SequenceDiff } from './algorithms/diffAlgorithm.js'; diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts index dbe71b0e7ad..f0088c3ef7f 100644 --- a/src/vs/editor/common/diff/rangeMapping.ts +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -9,7 +9,7 @@ import { BugIndicatingError } from '../../../base/common/errors.js'; import { LineRange } from '../core/ranges/lineRange.js'; import { Position } from '../core/position.js'; import { Range } from '../core/range.js'; -import { AbstractText, SingleTextEdit, TextEdit } from '../core/textEdit.js'; +import { AbstractText, SingleTextEdit, TextEdit } from '../core/edits/textEdit.js'; import { IChange } from './legacyLinesDiffComputer.js'; /** diff --git a/src/vs/editor/common/model/textModelOffsetEdit.ts b/src/vs/editor/common/model/textModelOffsetEdit.ts index 4cc02d88c2e..c49ea7d6fae 100644 --- a/src/vs/editor/common/model/textModelOffsetEdit.ts +++ b/src/vs/editor/common/model/textModelOffsetEdit.ts @@ -5,7 +5,7 @@ import { EditOperation } from '../core/editOperation.js'; import { Range } from '../core/range.js'; -import { OffsetEdit, SingleOffsetEdit } from '../core/offsetEdit.js'; +import { OffsetEdit, SingleOffsetEdit } from '../core/edits/offsetEdit.js'; import { OffsetRange } from '../core/offsetRange.js'; import { DetailedLineRangeMapping } from '../diff/rangeMapping.js'; import { ITextModel, IIdentifiedSingleEditOperation } from '../model.js'; diff --git a/src/vs/editor/common/model/textModelText.ts b/src/vs/editor/common/model/textModelText.ts index 0a4d6de5364..a580ae3c1e0 100644 --- a/src/vs/editor/common/model/textModelText.ts +++ b/src/vs/editor/common/model/textModelText.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Range } from '../core/range.js'; -import { AbstractText } from '../core/textEdit.js'; +import { AbstractText } from '../core/edits/textEdit.js'; import { TextLength } from '../core/textLength.js'; import { ITextModel } from '../model.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts index a47deb89b84..ee9edd4753f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts @@ -14,8 +14,8 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { InlineCompletionsModel } from './model/inlineCompletionsModel.js'; -import { TextEdit } from '../../../common/core/textEdit.js'; -import { LineEdit } from '../../../common/core/lineEdit.js'; +import { TextEdit } from '../../../common/core/edits/textEdit.js'; +import { LineEdit } from '../../../common/core/edits/lineEdit.js'; import { TextModelText } from '../../../common/model/textModelText.js'; export class InlineCompletionsAccessibleView implements IAccessibleViewImplementation { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts index 2e9b53e49a1..d71e46456f7 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts @@ -7,7 +7,7 @@ import { IDiffChange, LcsDiff } from '../../../../../base/common/diff/diff.js'; import { getLeadingWhitespace } from '../../../../../base/common/strings.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { ITextModel } from '../../../../common/model.js'; import { GhostText, GhostTextPart } from './ghostText.js'; import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts index f74a1e1a90e..73dc371b7d9 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts @@ -7,7 +7,7 @@ import { equals } from '../../../../../base/common/arrays.js'; import { splitLines } from '../../../../../base/common/strings.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit, TextEdit } from '../../../../common/core/edits/textEdit.js'; import { LineDecoration } from '../../../../common/viewLayout/lineDecorations.js'; import { InlineDecoration } from '../../../../common/viewModel.js'; import { ColumnRange } from '../../../../common/core/ranges/columnRange.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index a1948beefa1..c0bb095a86d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -23,7 +23,7 @@ import { LineRange } from '../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; import { Selection } from '../../../../common/core/selection.js'; -import { SingleTextEdit, TextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit, TextEdit } from '../../../../common/core/edits/textEdit.js'; import { TextLength } from '../../../../common/core/textLength.js'; import { ScrollType } from '../../../../common/editorCommon.js'; import { Command, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletionTriggerKind, PartialAcceptTriggerKind, InlineCompletionsProvider } from '../../../../common/languages.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts index 9ff5450279c..4b30c4446cf 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts @@ -16,7 +16,7 @@ import { IConfigurationService } from '../../../../../platform/configuration/com import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; -import { OffsetEdit } from '../../../../common/core/offsetEdit.js'; +import { OffsetEdit } from '../../../../common/core/edits/offsetEdit.js'; import { Position } from '../../../../common/core/position.js'; import { InlineCompletionEndOfLifeReasonKind, InlineCompletionContext, InlineCompletionTriggerKind, InlineCompletionsProvider } from '../../../../common/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts index f7ba383b8f4..03baa11ae28 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { Command } from '../../../../common/languages.js'; import { InlineSuggestionItem } from './inlineSuggestionItem.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts index e930859c2e7..58f1e932bab 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts @@ -9,12 +9,12 @@ import { observableSignal, IObservable } from '../../../../../base/common/observ import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../../../base/common/strings.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { ISingleEditOperation } from '../../../../common/core/editOperation.js'; -import { applyEditsToRanges, OffsetEdit, SingleOffsetEdit } from '../../../../common/core/offsetEdit.js'; +import { applyEditsToRanges, OffsetEdit, SingleOffsetEdit } from '../../../../common/core/edits/offsetEdit.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Position } from '../../../../common/core/position.js'; import { getPositionOffsetTransformerFromTextModel, PositionOffsetTransformerBase } from '../../../../common/core/positionToOffset.js'; import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit, StringText, TextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit, StringText, TextEdit } from '../../../../common/core/edits/textEdit.js'; import { TextLength } from '../../../../common/core/textLength.js'; import { linesDiffComputers } from '../../../../common/diff/linesDiffComputers.js'; import { InlineCompletion, InlineCompletionTriggerKind, Command, InlineCompletionWarning, PartialAcceptInfo, InlineCompletionEndOfLifeReason } from '../../../../common/languages.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts index 1cfd4b6aa91..3b9203b2e04 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts @@ -12,11 +12,11 @@ import { SetMap } from '../../../../../base/common/map.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { ISingleEditOperation } from '../../../../common/core/editOperation.js'; -import { SingleOffsetEdit } from '../../../../common/core/offsetEdit.js'; +import { SingleOffsetEdit } from '../../../../common/core/edits/offsetEdit.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletionProviderGroupId, InlineCompletions, InlineCompletionsProvider, InlineCompletionTriggerKind, PartialAcceptInfo } from '../../../../common/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { ITextModel } from '../../../../common/model.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts index 3c4d51ef8d9..06ff9987fe2 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts @@ -6,7 +6,7 @@ import { commonPrefixLength } from '../../../../../base/common/strings.js'; import { Range } from '../../../../common/core/range.js'; import { TextLength } from '../../../../common/core/textLength.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { EndOfLinePreference, ITextModel } from '../../../../common/model.js'; export function singleTextRemoveCommonPrefix(edit: SingleTextEdit, model: ITextModel, validModelRange?: Range): SingleTextEdit { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts index bb20091f146..0c945564266 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts @@ -10,7 +10,7 @@ import { Disposable } from '../../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { CompletionItemInsertTextRule, CompletionItemKind, SelectedSuggestionInfo } from '../../../../common/languages.js'; import { ITextModel } from '../../../../common/model.js'; import { singleTextEditAugments, singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts index bb9c627f3fd..c415242aa62 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts @@ -12,7 +12,7 @@ import { bindContextKey } from '../../../../platform/observable/common/platformO import { Position } from '../../../common/core/position.js'; import { PositionOffsetTransformer } from '../../../common/core/positionToOffset.js'; import { Range } from '../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; +import { SingleTextEdit, TextEdit } from '../../../common/core/edits/textEdit.js'; const array: ReadonlyArray = []; export function getReadonlyEmptyArray(): readonly T[] { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts index c26c6e3034c..164cfe03ec1 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts @@ -15,7 +15,7 @@ import { applyFontInfo } from '../../../../../browser/config/domFontInfo.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidgetPosition, IViewZoneChangeAccessor, MouseTargetType } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from '../../../../../common/config/editorOptions.js'; -import { OffsetEdit, SingleOffsetEdit } from '../../../../../common/core/offsetEdit.js'; +import { OffsetEdit, SingleOffsetEdit } from '../../../../../common/core/edits/offsetEdit.js'; import { Position } from '../../../../../common/core/position.js'; import { Range } from '../../../../../common/core/range.js'; import { StringBuilder } from '../../../../../common/core/stringBuilder.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts index 2041582469a..8c5151cbc9d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleLineEdit } from '../../../../../common/core/lineEdit.js'; +import { SingleLineEdit } from '../../../../../common/core/edits/lineEdit.js'; import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../../common/core/position.js'; -import { AbstractText, TextEdit } from '../../../../../common/core/textEdit.js'; +import { AbstractText, TextEdit } from '../../../../../common/core/edits/textEdit.js'; import { Command } from '../../../../../common/languages.js'; import { InlineSuggestionItem } from '../../model/inlineSuggestionItem.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts index 6498fd5340a..de7526b0098 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts @@ -9,7 +9,7 @@ import { localize } from '../../../../../../nls.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; -import { StringText, TextEdit } from '../../../../../common/core/textEdit.js'; +import { StringText, TextEdit } from '../../../../../common/core/edits/textEdit.js'; import { Command, InlineCompletionDisplayLocation } from '../../../../../common/languages.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { InlineCompletionItem } from '../../model/inlineSuggestionItem.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts index 6b1be8663d8..d57eccc5990 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts @@ -15,7 +15,7 @@ import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { Position } from '../../../../../common/core/position.js'; import { Range } from '../../../../../common/core/range.js'; -import { AbstractText, SingleTextEdit, StringText } from '../../../../../common/core/textEdit.js'; +import { AbstractText, SingleTextEdit, StringText } from '../../../../../common/core/edits/textEdit.js'; import { TextLength } from '../../../../../common/core/textLength.js'; import { DetailedLineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { TextModel } from '../../../../../common/model/textModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts index d170cc6c437..cd410a053a4 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts @@ -11,7 +11,7 @@ import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { LineRange } from '../../../../../common/core/ranges/lineRange.js'; import { Range } from '../../../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../../../common/core/textEdit.js'; +import { SingleTextEdit, TextEdit } from '../../../../../common/core/edits/textEdit.js'; import { TextModelText } from '../../../../../common/model/textModelText.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { InlineEdit } from '../../model/inlineEdit.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts index 7616d71cfd7..ac60eb381de 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts @@ -14,7 +14,7 @@ import { Point } from '../../../../../../common/core/2d/point.js'; import { Rect } from '../../../../../../common/core/2d/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { SingleTextEdit } from '../../../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../../../common/core/edits/textEdit.js'; import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getModifiedBorderColor } from '../theme.js'; import { mapOutFalsy, rectToProps } from '../utils/utils.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts index 55339389d22..d9cc00e40c5 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts @@ -15,9 +15,9 @@ import { Point } from '../../../../../../common/core/2d/point.js'; import { Rect } from '../../../../../../common/core/2d/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { SingleOffsetEdit } from '../../../../../../common/core/offsetEdit.js'; +import { SingleOffsetEdit } from '../../../../../../common/core/edits/offsetEdit.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { SingleTextEdit } from '../../../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../../../common/core/edits/textEdit.js'; import { ILanguageService } from '../../../../../../common/languages/language.js'; import { LineTokens } from '../../../../../../common/tokens/lineTokens.js'; import { TokenArray } from '../../../../../../common/tokens/tokenArray.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts index 63b438213af..0510e23b849 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts @@ -12,7 +12,7 @@ import { observableCodeEditor } from '../../../../../../browser/observableCodeEd import { rangeIsSingleLine } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Range } from '../../../../../../common/core/range.js'; -import { AbstractText } from '../../../../../../common/core/textEdit.js'; +import { AbstractText } from '../../../../../../common/core/edits/textEdit.js'; import { DetailedLineRangeMapping } from '../../../../../../common/diff/rangeMapping.js'; import { EndOfLinePreference, IModelDeltaDecoration, InjectedTextCursorStops, ITextModel } from '../../../../../../common/model.js'; import { ModelDecorationOptions } from '../../../../../../common/model/textModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts index e2dc6695af6..15a4269393d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts @@ -22,7 +22,7 @@ import { LineRange } from '../../../../../../common/core/ranges/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Position } from '../../../../../../common/core/position.js'; import { Range } from '../../../../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../../../../common/core/textEdit.js'; +import { SingleTextEdit, TextEdit } from '../../../../../../common/core/edits/textEdit.js'; import { RangeMapping } from '../../../../../../common/diff/rangeMapping.js'; import { ITextModel } from '../../../../../../common/model.js'; import { indentOfLine } from '../../../../../../common/model/textModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/computeGhostText.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/computeGhostText.test.ts index c6e68393ab1..059f5fdb964 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/computeGhostText.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/computeGhostText.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; import { computeGhostText } from '../../browser/model/computeGhostText.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/getSecondaryEdits.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/getSecondaryEdits.test.ts index b418f100b03..5e3351542dd 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/getSecondaryEdits.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/getSecondaryEdits.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { Position } from '../../../../common/core/position.js'; import { getSecondaryEdits } from '../../browser/model/inlineCompletionsModel.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; import { Range } from '../../../../common/core/range.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts index ffbb9897aa6..dcfffc1b208 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts @@ -21,7 +21,7 @@ import { LanguageFeaturesService } from '../../../../common/services/languageFea import { ViewModel } from '../../../../common/viewModel/viewModelImpl.js'; import { InlineCompletionsController } from '../../browser/controller/inlineCompletionsController.js'; import { Range } from '../../../../common/core/range.js'; -import { TextEdit } from '../../../../common/core/textEdit.js'; +import { TextEdit } from '../../../../common/core/edits/textEdit.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { PositionOffsetTransformer } from '../../../../common/core/positionToOffset.js'; diff --git a/src/vs/editor/test/common/core/random.ts b/src/vs/editor/test/common/core/random.ts index 7e02e9c2a62..6de65b59859 100644 --- a/src/vs/editor/test/common/core/random.ts +++ b/src/vs/editor/test/common/core/random.ts @@ -5,12 +5,12 @@ import { numberComparator } from '../../../../base/common/arrays.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; -import { OffsetEdit, SingleOffsetEdit } from '../../../common/core/offsetEdit.js'; +import { OffsetEdit, SingleOffsetEdit } from '../../../common/core/edits/offsetEdit.js'; import { OffsetRange } from '../../../common/core/offsetRange.js'; import { Position } from '../../../common/core/position.js'; import { PositionOffsetTransformer } from '../../../common/core/positionToOffset.js'; import { Range } from '../../../common/core/range.js'; -import { AbstractText, SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; +import { AbstractText, SingleTextEdit, TextEdit } from '../../../common/core/edits/textEdit.js'; export abstract class Random { public static readonly alphabetSmallLowercase = 'abcdefgh'; diff --git a/src/vs/editor/test/common/core/textEdit.test.ts b/src/vs/editor/test/common/core/textEdit.test.ts index c048f0fbd97..6b36ce1ca1d 100644 --- a/src/vs/editor/test/common/core/textEdit.test.ts +++ b/src/vs/editor/test/common/core/textEdit.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { OffsetRange } from '../../../common/core/offsetRange.js'; -import { StringText } from '../../../common/core/textEdit.js'; +import { StringText } from '../../../common/core/edits/textEdit.js'; import { Random } from './random.js'; suite('TextEdit', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts index 2d60eb41508..bda37843275 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../common/core/edits/textEdit.js'; import { TextEditInfo } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.js'; import { combineTextEditInfos } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.js'; import { lengthAdd, lengthToObj, lengthToPosition, positionToLength, toLength } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; diff --git a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts index 7e93197efec..4ecde34435e 100644 --- a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts +++ b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts @@ -11,7 +11,7 @@ import { LinesSliceCharSequence } from '../../../common/diff/defaultLinesDiffCom import { MyersDiffAlgorithm } from '../../../common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.js'; import { DynamicProgrammingDiffing } from '../../../common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ArrayText } from '../../../common/core/textEdit.js'; +import { ArrayText } from '../../../common/core/edits/textEdit.js'; suite('myers', () => { ensureNoDisposablesAreLeakedInTestSuite(); diff --git a/src/vs/editor/test/node/diffing/fixtures.test.ts b/src/vs/editor/test/node/diffing/fixtures.test.ts index e85d69f1260..34be0e4398c 100644 --- a/src/vs/editor/test/node/diffing/fixtures.test.ts +++ b/src/vs/editor/test/node/diffing/fixtures.test.ts @@ -13,7 +13,7 @@ import { LegacyLinesDiffComputer } from '../../../common/diff/legacyLinesDiffCom import { DefaultLinesDiffComputer } from '../../../common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.js'; import { Range } from '../../../common/core/range.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { AbstractText, ArrayText, SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; +import { AbstractText, ArrayText, SingleTextEdit, TextEdit } from '../../../common/core/edits/textEdit.js'; import { LinesDiff } from '../../../common/diff/linesDiffComputer.js'; suite('diffing fixtures', () => { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts index 781cc373a67..034cb8395fe 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts @@ -13,7 +13,7 @@ import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { getCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { EditOperation, ISingleEditOperation } from '../../../../../editor/common/core/editOperation.js'; -import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; +import { OffsetEdit } from '../../../../../editor/common/core/edits/offsetEdit.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { IDocumentDiff, nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index d7757997f32..eef68bc8571 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts @@ -10,7 +10,7 @@ import { Schemas } from '../../../../../base/common/network.js'; import { clamp } from '../../../../../base/common/numbers.js'; import { autorun, derived, IObservable, ITransaction, observableValue, observableValueOpts } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; -import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; +import { OffsetEdit } from '../../../../../editor/common/core/edits/offsetEdit.js'; import { TextEdit } from '../../../../../editor/common/languages.js'; import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts index 8fec90b3e67..f52ef2821cf 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts @@ -15,7 +15,7 @@ import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { LineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; -import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; +import { OffsetEdit } from '../../../../../editor/common/core/edits/offsetEdit.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping, RangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSessionStorage.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSessionStorage.ts index 7eba4f40c87..d28f865976e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSessionStorage.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSessionStorage.ts @@ -8,7 +8,7 @@ import { StringSHA1 } from '../../../../../base/common/hash.js'; import { ResourceMap } from '../../../../../base/common/map.js'; import { joinPath } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; -import { OffsetEdit, ISingleOffsetEdit, IOffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; +import { OffsetEdit, ISingleOffsetEdit, IOffsetEdit } from '../../../../../editor/common/core/edits/offsetEdit.js'; import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts index 1697e227fe6..17e5ce86dc1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts @@ -10,7 +10,7 @@ import { ObservableDisposable } from '../../../../../../base/common/observableDi import { themeColorFromId } from '../../../../../../base/common/themables.js'; import { URI } from '../../../../../../base/common/uri.js'; import { EditOperation, ISingleEditOperation } from '../../../../../../editor/common/core/editOperation.js'; -import { OffsetEdit } from '../../../../../../editor/common/core/offsetEdit.js'; +import { OffsetEdit } from '../../../../../../editor/common/core/edits/offsetEdit.js'; import { Range } from '../../../../../../editor/common/core/range.js'; import { IDocumentDiff, nullDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; diff --git a/src/vs/workbench/contrib/chat/test/browser/chatEditingSessionStorage.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatEditingSessionStorage.test.ts index cc036e2f6e4..bbafcaad2ac 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatEditingSessionStorage.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatEditingSessionStorage.test.ts @@ -9,7 +9,7 @@ import { cloneAndChange } from '../../../../../base/common/objects.js'; import { URI } from '../../../../../base/common/uri.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; +import { OffsetEdit } from '../../../../../editor/common/core/edits/offsetEdit.js'; import { OffsetRange } from '../../../../../editor/common/core/offsetRange.js'; import { FileService } from '../../../../../platform/files/common/fileService.js'; import { InMemoryFileSystemProvider } from '../../../../../platform/files/common/inMemoryFilesystemProvider.js'; From c67e9f96ffebe78a8b7100c8448f047b103b4886 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 7 May 2025 08:58:34 -0700 Subject: [PATCH 88/90] mcp: finalize MCP server definition provider API (#248244) * mcp: finalize MCP server definition provider API Closes #243522 * comments --- .../common/extensionsApiProposals.ts | 3 - .../workbench/api/common/extHost.api.impl.ts | 4 - src/vscode-dts/vscode.d.ts | 161 +++++++++++++++++ ...ode.proposed.mcpConfigurationProvider.d.ts | 167 ------------------ 4 files changed, 161 insertions(+), 174 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index c43841d4784..77d4f0f0b20 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -242,9 +242,6 @@ const _allApiProposals = { mappedEditsProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts', }, - mcpConfigurationProvider: { - proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts', - }, multiDocumentHighlightProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts', }, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 9b9274fdfd6..1f8c97d2642 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1528,14 +1528,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostLanguageModels.registerIgnoredFileProvider(extension, provider); }, registerMcpServerDefinitionProvider(id, provider) { - checkProposedApiEnabled(extension, 'mcpConfigurationProvider'); return extHostMcp.registerMcpConfigurationProvider(extension, id, provider); } }; - // todo@connor4312: proposed API back-compat - (lm as any).registerMcpConfigurationProvider = lm.registerMcpServerDefinitionProvider; - // namespace: speech const speech: typeof vscode.speech = { registerSpeechProvider(id: string, provider: vscode.SpeechProvider) { diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 7a37f98d239..16cb98ca7c9 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -20213,6 +20213,139 @@ declare module 'vscode' { toolMode?: LanguageModelChatToolMode; } + /** + * McpStdioServerDefinition represents an MCP server available by running + * a local process and operating on its stdin and stdout streams. The process + * will be spawned as a child process of the extension host and by default + * will not run in a shell environment. + */ + export class McpStdioServerDefinition { + /** + * The human-readable name of the server. + */ + readonly label: string; + + /** + * The working directory used to start the server. + */ + cwd?: Uri; + + /** + * The command used to start the server. Node.js-based servers may use + * `process.execPath` to use the editor's version of Node.js to run the script. + */ + command: string; + + /** + * Additional command-line arguments passed to the server. + */ + args: string[]; + + /** + * Optional additional environment information for the server. Variables + * in this environment will overwrite or remove (if null) the default + * environment variables of the editor's extension host. + */ + env: Record; + + /** + * Optional version identification for the server. If this changes, the + * editor will indicate that tools have changed and prompt to refresh them. + */ + version?: string; + + /** + * @param label The human-readable name of the server. + * @param command The command used to start the server. + * @param args Additional command-line arguments passed to the server. + * @param env Optional additional environment information for the server. + * @param version Optional version identification for the server. + */ + constructor(label: string, command: string, args?: string[], env?: Record, version?: string); + } + + /** + * McpHttpServerDefinition represents an MCP server available using the + * Streamable HTTP transport. + */ + export class McpHttpServerDefinition { + /** + * The human-readable name of the server. + */ + readonly label: string; + + /** + * The URI of the server. The editor will make a POST request to this URI + * to begin each session. + */ + uri: Uri; + + /** + * Optional additional heads included with each request to the server. + */ + headers: Record; + + /** + * Optional version identification for the server. If this changes, the + * editor will indicate that tools have changed and prompt to refresh them. + */ + version?: string; + + /** + * @param label The human-readable name of the server. + * @param uri The URI of the server. + * @param headers Optional additional heads included with each request to the server. + */ + constructor(label: string, uri: Uri, headers?: Record, version?: string); + } + + /** + * Definitions that describe different types of Model Context Protocol servers, + * which can be returned from the {@link McpServerDefinitionProvider}. + */ + export type McpServerDefinition = McpStdioServerDefinition | McpHttpServerDefinition; + + /** + * A type that can provide Model Context Protocol server definitions. This + * should be registered using {@link lm.registerMcpServerDefinitionProvider} + * during extension activation. + */ + export interface McpServerDefinitionProvider { + /** + * Optional event fired to signal that the set of available servers has changed. + */ + readonly onDidChangeMcpServerDefinitions?: Event; + + /** + * Provides available MCP servers. The editor will call this method eagerly + * to ensure the availability of servers for the language model, and so + * extensions should not take actions which would require user + * interaction, such as authentication. + * + * @param token A cancellation token. + * @returns An array of MCP available MCP servers + */ + provideMcpServerDefinitions(token: CancellationToken): ProviderResult; + + /** + * This function will be called when the editor needs to start a MCP server. + * At this point, the extension may take any actions which may require user + * interaction, such as authentication. Any non-`readonly` property of the + * server may be modified, and the extension should return the resolved server. + * + * The extension may return undefined to indicate that the server + * should not be started, or throw an error. If there is a pending tool + * call, the editor will cancel it and return an error message to the + * language model. + * + * @param server The MCP server to resolve + * @param token A cancellation token. + * @returns The resolved server or thenable that resolves to such. This may + * be the given `server` definition with non-readonly properties filled in. + */ + resolveMcpServerDefinition?(server: T, token: CancellationToken): ProviderResult; + } + /** * Namespace for language model related functionality. */ @@ -20291,6 +20424,34 @@ declare module 'vscode' { * @returns The result of the tool invocation. */ export function invokeTool(name: string, options: LanguageModelToolInvocationOptions, token?: CancellationToken): Thenable; + + /** + * Registers a provider that publishes Model Context Protocol servers for the editor to + * consume. This allows MCP servers to be dynamically provided to the editor in + * addition to those the user creates in their configuration files. + * + * Before calling this method, extensions must register the `contributes.mcpServerDefinitionProviders` + * extension point with the corresponding {@link id}, for example: + * + * ```js + * "contributes": { + * "mcpServerDefinitionProviders": [ + * { + * "id": "cool-cloud-registry.mcp-servers", + * "label": "Cool Cloud Registry", + * } + * ] + * } + * ``` + * + * When a new McpServerDefinitionProvider is available, the editor will present a 'refresh' + * action to the user to discover new servers. To enable this flow, extensions should + * call `registerMcpServerDefinitionProvider` during activation. + * @param id The ID of the provider, which is unique to the extension. + * @param provider The provider to register + * @returns A disposable that unregisters the provider when disposed. + */ + export function registerMcpServerDefinitionProvider(id: string, provider: McpServerDefinitionProvider): Disposable; } /** diff --git a/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts b/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts deleted file mode 100644 index 11d6e1873e0..00000000000 --- a/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts +++ /dev/null @@ -1,167 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/243522 - - /** - * McpStdioServerDefinition represents an MCP server available by running - * a local process and operating on its stdin and stdout streams. The process - * will be spawned as a child process of the extension host and by default - * will not run in a shell environment. - */ - export class McpStdioServerDefinition { - /** - * The human-readable name of the server. - */ - readonly label: string; - - /** - * The working directory used to start the server. - */ - cwd?: Uri; - - /** - * The command used to start the server. Node.js-based servers may use - * `process.execPath` to use the editor's version of Node.js to run the script. - */ - command: string; - - /** - * Additional command-line arguments passed to the server. - */ - args: string[]; - - /** - * Optional additional environment information for the server. Variables - * in this environment will overwrite or remove (if null) the default - * environment variables of the editor's extension host. - */ - env: Record; - - /** - * Optional version identification for the server. If this changes, the - * editor will indicate that tools have changed and prompt to refresh them. - */ - version?: string; - - /** - * @param label The human-readable name of the server. - * @param command The command used to start the server. - * @param args Additional command-line arguments passed to the server. - * @param env Optional additional environment information for the server. - * @param version Optional version identification for the server. - */ - constructor(label: string, command: string, args?: string[], env?: Record, version?: string); - } - - /** - * McpHttpServerDefinition represents an MCP server available using the - * Streamable HTTP transport. - */ - export class McpHttpServerDefinition { - /** - * The human-readable name of the server. - */ - readonly label: string; - - /** - * The URI of the server. The editor will make a POST request to this URI - * to begin each session. - */ - uri: Uri; - - /** - * Optional additional heads included with each request to the server. - */ - headers: Record; - - /** - * Optional version identification for the server. If this changes, the - * editor will indicate that tools have changed and prompt to refresh them. - */ - version?: string; - - /** - * @param label The human-readable name of the server. - * @param uri The URI of the server. - * @param headers Optional additional heads included with each request to the server. - */ - constructor(label: string, uri: Uri, headers?: Record, version?: string); - } - - /** - * Definitions that describe different types of Model Context Protocol servers, - * which can be returned from the {@link McpServerDefinitionProvider}. - */ - export type McpServerDefinition = McpStdioServerDefinition | McpHttpServerDefinition; - - /** - * A type that can provide Model Context Protocol server definitions. This - * should be registered using {@link lm.registerMcpServerDefinitionProvider} - * during extension activation. - */ - export interface McpServerDefinitionProvider { - /** - * Optional event fired to signal that the set of available servers has changed. - */ - onDidChangeMcpServerDefinitions?: Event; - - /** - * Provides available MCP servers. The editor will call this method eagerly - * to ensure the availability of servers for the language model, and so - * extensions should not take actions which would require user - * interaction, such as authentication. - * - * @param token A cancellation token. - * @returns An array of MCP available MCP servers - */ - provideMcpServerDefinitions(token: CancellationToken): ProviderResult; - - /** - * This function will be called when the editor needs to start MCP server. - * At this point, the extension may take any actions which may require user - * interaction, such as authentication. Any non-`readonly` property of the - * server may be modified, and the extension may return a new server. - * - * The extension may return undefined to indicate that the server - * should not be started, or throw an error. If there is a pending tool - * call, the editor will cancel it and return an error message to the - * language model. - * - * @param server The MCP server to resolve - * @param token A cancellation token. - * @returns The resolved server or thenable that resolves to such. - */ - resolveMcpServerDefinition?(server: T, token: CancellationToken): ProviderResult; - } - - namespace lm { - /** - * Registers a provider that publishes Model Context Protocol servers for the editor to - * consume. This allows MCP servers to be dynamically provided to the editor in - * addition to those the user creates in their configuration files. - * - * Before calling this method, extensions must register the `contributes.mcpServerDefinitionProviders` - * extension point with the corresponding {@link id}, for example: - * - * ```js - * "contributes": { - * "mcpServerDefinitionProviders": [ - * { - * "id": "cool-cloud-registry.mcp-servers", - * "label": "Cool Cloud Registry", - * } - * ] - * } - * ``` - * - * When a new McpServerDefinitionProvider is available, the editor will present a 'refresh' - * action to the user to discover new servers. To enable this flow, extensions should - * call `registerMcpServerDefinitionProvider` during activation. - */ - export function registerMcpServerDefinitionProvider(id: string, provider: McpServerDefinitionProvider): Disposable; - } -} From a701aa1e6809b812e94e0f0ea57bd0b7da7f5e6e Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 7 May 2025 18:49:32 +0200 Subject: [PATCH 89/90] Align merge editor LineRange and common LineRange. --- .../features/hideUnchangedRegionsFeature.ts | 4 +- src/vs/editor/common/core/ranges/lineRange.ts | 21 ++-- src/vs/editor/common/diff/rangeMapping.ts | 4 +- .../mergeMarkers/mergeMarkersController.ts | 12 +- .../mergeEditor/browser/model/diffComputer.ts | 8 +- .../mergeEditor/browser/model/editing.ts | 6 +- .../mergeEditor/browser/model/lineRange.ts | 111 ++++-------------- .../mergeEditor/browser/model/mapping.ts | 38 +++--- .../browser/model/mergeEditorModel.ts | 24 ++-- .../browser/model/modifiedBaseRange.ts | 12 +- .../browser/model/textModelDiffs.ts | 20 ++-- .../mergeEditor/browser/view/editorGutter.ts | 10 +- .../view/editors/resultCodeEditorView.ts | 6 +- .../mergeEditor/browser/view/lineAlignment.ts | 4 +- .../mergeEditor/browser/view/viewModel.ts | 6 +- .../mergeEditor/browser/view/viewZones.ts | 6 +- .../mergeEditor/test/browser/model.test.ts | 8 +- .../contrib/scm/browser/quickDiffDecorator.ts | 2 +- .../contrib/scm/browser/quickDiffWidget.ts | 2 +- 19 files changed, 119 insertions(+), 185 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts index 8212330fc5c..a1c54c56381 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts @@ -243,7 +243,7 @@ export class HideUnchangedRegionsFeature extends Disposable { const lineNumber = event.target.position.lineNumber; const model = this._diffModel.get(); if (!model) { return; } - const region = model.unchangedRegions.get().find(r => r.modifiedUnchangedRange.includes(lineNumber)); + const region = model.unchangedRegions.get().find(r => r.modifiedUnchangedRange.contains(lineNumber)); if (!region) { return; } region.collapseAll(undefined); event.event.stopPropagation(); @@ -256,7 +256,7 @@ export class HideUnchangedRegionsFeature extends Disposable { const lineNumber = event.target.position.lineNumber; const model = this._diffModel.get(); if (!model) { return; } - const region = model.unchangedRegions.get().find(r => r.originalUnchangedRange.includes(lineNumber)); + const region = model.unchangedRegions.get().find(r => r.originalUnchangedRange.contains(lineNumber)); if (!region) { return; } region.collapseAll(undefined); event.event.stopPropagation(); diff --git a/src/vs/editor/common/core/ranges/lineRange.ts b/src/vs/editor/common/core/ranges/lineRange.ts index 1eacb1d54ac..3d0791dfb35 100644 --- a/src/vs/editor/common/core/ranges/lineRange.ts +++ b/src/vs/editor/common/core/ranges/lineRange.ts @@ -7,11 +7,16 @@ import { BugIndicatingError } from '../../../../base/common/errors.js'; import { OffsetRange } from '../offsetRange.js'; import { Range } from '../range.js'; import { findFirstIdxMonotonousOrArrLen, findLastIdxMonotonous, findLastMonotonous } from '../../../../base/common/arraysFind.js'; +import { Comparator, compareBy, numberComparator } from '../../../../base/common/arrays.js'; /** * A range of lines (1-based). */ export class LineRange { + public static ofLength(startLineNumber: number, length: number): LineRange { + return new LineRange(startLineNumber, startLineNumber + length); + } + public static fromRange(range: Range): LineRange { return new LineRange(range.startLineNumber, range.endLineNumber); } @@ -20,6 +25,8 @@ export class LineRange { return new LineRange(range.startLineNumber, range.endLineNumber + 1); } + public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); + public static subtract(a: LineRange, b: LineRange | undefined): LineRange[] { if (!b) { return [a]; @@ -65,10 +72,6 @@ export class LineRange { return new LineRange(startLineNumber, endLineNumberExclusive); } - public static ofLength(startLineNumber: number, length: number): LineRange { - return new LineRange(startLineNumber, startLineNumber + length); - } - /** * @internal */ @@ -104,6 +107,10 @@ export class LineRange { return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; } + public containsRange(range: LineRange): boolean { + return this.startLineNumber <= range.startLineNumber && range.endLineNumberExclusive <= this.endLineNumberExclusive; + } + /** * Indicates if this line range is empty. */ @@ -160,7 +167,7 @@ export class LineRange { return this.startLineNumber < other.endLineNumberExclusive && other.startLineNumber < this.endLineNumberExclusive; } - public overlapOrTouch(other: LineRange): boolean { + public intersectsOrTouches(other: LineRange): boolean { return this.startLineNumber <= other.endLineNumberExclusive && other.startLineNumber <= this.endLineNumberExclusive; } @@ -203,10 +210,6 @@ export class LineRange { return [this.startLineNumber, this.endLineNumberExclusive]; } - public includes(lineNumber: number): boolean { - return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; - } - /** * Converts this 1-based line range to a 0-based offset range (subtracts 1!). * @internal diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts index f0088c3ef7f..4dfbd9a36d7 100644 --- a/src/vs/editor/common/diff/rangeMapping.ts +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -312,8 +312,8 @@ export function lineRangeMappingFromRangeMappings(alignments: readonly RangeMapp for (const g of groupAdjacentBy( alignments.map(a => getLineRangeMapping(a, originalLines, modifiedLines)), (a1, a2) => - a1.original.overlapOrTouch(a2.original) - || a1.modified.overlapOrTouch(a2.modified) + a1.original.intersectsOrTouches(a2.original) + || a1.modified.intersectsOrTouches(a2.modified) )) { const first = g[0]; const last = g[g.length - 1]; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts index d2611d0b1f8..f915f609d19 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts @@ -8,7 +8,7 @@ import { Disposable, DisposableStore } from '../../../../../base/common/lifecycl import { autorun, IObservable } from '../../../../../base/common/observable.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { ITextModel } from '../../../../../editor/common/model.js'; -import { LineRange } from '../model/lineRange.js'; +import { MergeEditorLineRange } from '../model/lineRange.js'; import { MergeEditorViewModel } from '../view/viewModel.js'; import * as nls from '../../../../../nls.js'; @@ -42,7 +42,7 @@ export class MergeMarkersController extends Disposable { const model = this.editor.getModel(); const blocks = model ? getBlocks(model, { blockToRemoveStartLinePrefix: conflictMarkers.start, blockToRemoveEndLinePrefix: conflictMarkers.end }) : { blocks: [] }; - this.editor.setHiddenAreas(blocks.blocks.map(b => b.lineRange.deltaEnd(-1).toRange()), this); + this.editor.setHiddenAreas(blocks.blocks.map(b => b.lineRange.deltaEnd(-1).toExclusiveRange()), this); this.editor.changeViewZones(c => { this.disposableStore.clear(); for (const id of this.viewZoneIds) { @@ -54,7 +54,7 @@ export class MergeMarkersController extends Disposable { const startLine = model!.getLineContent(b.lineRange.startLineNumber).substring(0, 20); const endLine = model!.getLineContent(b.lineRange.endLineNumberExclusive - 1).substring(0, 20); - const conflictingLinesCount = b.lineRange.lineCount - 2; + const conflictingLinesCount = b.lineRange.length - 2; const domNode = h('div', [ h('div.conflict-zone-root', [ @@ -100,7 +100,7 @@ export class MergeMarkersController extends Disposable { if (activeRange) { const activeRangeInResult = vm.model.getLineRangeInResult(activeRange.baseRange, reader); - if (activeRangeInResult.intersects(b.lineRange)) { + if (activeRangeInResult.intersectsOrTouches(b.lineRange)) { classNames.push('focused'); } } @@ -133,7 +133,7 @@ function getBlocks(document: ITextModel, configuration: ProjectionConfiguration) } else { if (line.startsWith(configuration.blockToRemoveEndLinePrefix)) { inBlock = false; - blocks.push(new Block(new LineRange(startLineNumber, curLine - startLineNumber + 1))); + blocks.push(new Block(MergeEditorLineRange.fromLength(startLineNumber, curLine - startLineNumber + 1))); transformedContent.push(''); } } @@ -146,7 +146,7 @@ function getBlocks(document: ITextModel, configuration: ProjectionConfiguration) } class Block { - constructor(public readonly lineRange: LineRange) { } + constructor(public readonly lineRange: MergeEditorLineRange) { } } interface ProjectionConfiguration { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts index 94ee5e1c408..93bc4dcc95c 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts @@ -9,10 +9,10 @@ import { RangeMapping as DiffRangeMapping } from '../../../../../editor/common/d import { ITextModel } from '../../../../../editor/common/model.js'; import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; -import { LineRange } from './lineRange.js'; +import { MergeEditorLineRange } from './lineRange.js'; import { DetailedLineRangeMapping, RangeMapping } from './mapping.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; -import { LineRange as DiffLineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; +import { LineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; export interface IMergeDiffComputer { computeDiff(textModel1: ITextModel, textModel2: ITextModel, reader: IReader): Promise; @@ -121,8 +121,8 @@ export class MergeDiffComputer implements IMergeDiffComputer { } } -export function toLineRange(range: DiffLineRange): LineRange { - return new LineRange(range.startLineNumber, range.length); +export function toLineRange(range: LineRange): MergeEditorLineRange { + return MergeEditorLineRange.fromLength(range.startLineNumber, range.length); } export function toRangeMapping(mapping: DiffRangeMapping): RangeMapping { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts index 3bbd65333ff..9f55a9fddf7 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts @@ -6,15 +6,15 @@ import { equals } from '../../../../../base/common/arrays.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { IIdentifiedSingleEditOperation } from '../../../../../editor/common/model.js'; -import { LineRange } from './lineRange.js'; +import { MergeEditorLineRange } from './lineRange.js'; /** * Represents an edit, expressed in whole lines: - * At (before) {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. + * At (before) {@link MergeEditorLineRange.startLineNumber}, delete {@link MergeEditorLineRange.length} many lines and insert {@link newLines}. */ export class LineRangeEdit { constructor( - public readonly range: LineRange, + public readonly range: MergeEditorLineRange, public readonly newLines: string[] ) { } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts index 8c9f2b57bf4..fe3559e8c3a 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts @@ -3,128 +3,59 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Comparator, compareBy, numberComparator } from '../../../../../base/common/arrays.js'; -import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { Constants } from '../../../../../base/common/uint.js'; import { Range } from '../../../../../editor/common/core/range.js'; +import { LineRange } from '../../../../../editor/common/core/ranges/lineRange.js'; import { ITextModel } from '../../../../../editor/common/model.js'; -export class LineRange { - public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); - - public static join(ranges: LineRange[]): LineRange | undefined { - if (ranges.length === 0) { - return undefined; - } - - let startLineNumber = Number.MAX_SAFE_INTEGER; - let endLineNumber = 0; - for (const range of ranges) { - startLineNumber = Math.min(startLineNumber, range.startLineNumber); - endLineNumber = Math.max(endLineNumber, range.startLineNumber + range.lineCount); - } - return new LineRange(startLineNumber, endLineNumber - startLineNumber); +/** + * TODO: Deprecate in favor of LineRange! + */ +export class MergeEditorLineRange extends LineRange { + static fromLineNumbers(startLineNumber: number, endExclusiveLineNumber: number): MergeEditorLineRange { + return MergeEditorLineRange.fromLength(startLineNumber, endExclusiveLineNumber - startLineNumber); } - static fromLineNumbers(startLineNumber: number, endExclusiveLineNumber: number): LineRange { - return new LineRange(startLineNumber, endExclusiveLineNumber - startLineNumber); + static fromLength(startLineNumber: number, length: number): MergeEditorLineRange { + return new MergeEditorLineRange(startLineNumber, startLineNumber + length); } - constructor( - public readonly startLineNumber: number, - public readonly lineCount: number - ) { - if (lineCount < 0) { - throw new BugIndicatingError(); - } + public override join(other: MergeEditorLineRange): MergeEditorLineRange { + return MergeEditorLineRange.fromLineNumbers(Math.min(this.startLineNumber, other.startLineNumber), Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive)); } - public join(other: LineRange): LineRange { - return LineRange.fromLineNumbers(Math.min(this.startLineNumber, other.startLineNumber), Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive)); - } - - public get endLineNumberExclusive(): number { - return this.startLineNumber + this.lineCount; - } - - public get isEmpty(): boolean { - return this.lineCount === 0; - } - - /** - * Returns false if there is at least one line between `this` and `other`. - */ - public touches(other: LineRange): boolean { - return ( - this.endLineNumberExclusive >= other.startLineNumber && - other.endLineNumberExclusive >= this.startLineNumber - ); - } - - public isAfter(range: LineRange): boolean { + public isAfter(range: MergeEditorLineRange): boolean { return this.startLineNumber >= range.endLineNumberExclusive; } - public isBefore(range: LineRange): boolean { + public isBefore(range: MergeEditorLineRange): boolean { return range.startLineNumber >= this.endLineNumberExclusive; } - public delta(lineDelta: number): LineRange { - return new LineRange(this.startLineNumber + lineDelta, this.lineCount); + public override delta(lineDelta: number): MergeEditorLineRange { + return MergeEditorLineRange.fromLength(this.startLineNumber + lineDelta, this.length); } - public toString() { - return `[${this.startLineNumber},${this.endLineNumberExclusive})`; + public deltaEnd(delta: number): MergeEditorLineRange { + return MergeEditorLineRange.fromLength(this.startLineNumber, this.length + delta); } - public equals(originalRange: LineRange) { - return this.startLineNumber === originalRange.startLineNumber && this.lineCount === originalRange.lineCount; - } - - public contains(lineNumber: number): boolean { - return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; - } - - public deltaEnd(delta: number): LineRange { - return new LineRange(this.startLineNumber, this.lineCount + delta); - } - - public deltaStart(lineDelta: number): LineRange { - return new LineRange(this.startLineNumber + lineDelta, this.lineCount - lineDelta); + public deltaStart(lineDelta: number): MergeEditorLineRange { + return MergeEditorLineRange.fromLength(this.startLineNumber + lineDelta, this.length - lineDelta); } public getLines(model: ITextModel): string[] { - const result = new Array(this.lineCount); - for (let i = 0; i < this.lineCount; i++) { + const result = new Array(this.length); + for (let i = 0; i < this.length; i++) { result[i] = model.getLineContent(this.startLineNumber + i); } return result; } - public containsRange(range: LineRange): boolean { - return this.startLineNumber <= range.startLineNumber && range.endLineNumberExclusive <= this.endLineNumberExclusive; - } - - public toRange(): Range { - return new Range(this.startLineNumber, 1, this.endLineNumberExclusive, 1); - } - - public toInclusiveRange(): Range | undefined { - if (this.isEmpty) { - return undefined; - } - return new Range(this.startLineNumber, 1, this.endLineNumberExclusive - 1, Constants.MAX_SAFE_SMALL_INTEGER); - } - public toInclusiveRangeOrEmpty(): Range { if (this.isEmpty) { return new Range(this.startLineNumber, 1, this.startLineNumber, 1); } return new Range(this.startLineNumber, 1, this.endLineNumberExclusive - 1, Constants.MAX_SAFE_SMALL_INTEGER); } - - intersects(lineRange: LineRange) { - return this.startLineNumber <= lineRange.endLineNumberExclusive - && lineRange.startLineNumber <= this.endLineNumberExclusive; - } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts index 13bd79f8584..0a52cc17b4f 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts @@ -11,7 +11,7 @@ import { Position } from '../../../../../editor/common/core/position.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { ITextModel } from '../../../../../editor/common/model.js'; import { LineRangeEdit } from './editing.js'; -import { LineRange } from './lineRange.js'; +import { MergeEditorLineRange } from './lineRange.js'; import { addLength, lengthBetweenPositions, rangeContainsPosition, rangeIsBeforeOrTouching } from './rangeUtils.js'; /** @@ -22,11 +22,11 @@ export class LineRangeMapping { return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); } constructor( - public readonly inputRange: LineRange, - public readonly outputRange: LineRange + public readonly inputRange: MergeEditorLineRange, + public readonly outputRange: MergeEditorLineRange ) { } - public extendInputRange(extendedInputRange: LineRange): LineRangeMapping { + public extendInputRange(extendedInputRange: MergeEditorLineRange): LineRangeMapping { if (!extendedInputRange.containsRange(this.inputRange)) { throw new BugIndicatingError(); } @@ -35,9 +35,9 @@ export class LineRangeMapping { const endDelta = extendedInputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; return new LineRangeMapping( extendedInputRange, - new LineRange( + MergeEditorLineRange.fromLength( this.outputRange.startLineNumber + startDelta, - this.outputRange.lineCount - startDelta + endDelta + this.outputRange.length - startDelta + endDelta ) ); } @@ -111,16 +111,16 @@ export class DocumentLineRangeMap { const lastBefore = findLast(this.lineRangeMappings, r => r.inputRange.startLineNumber <= lineNumber); if (!lastBefore) { return new LineRangeMapping( - new LineRange(lineNumber, 1), - new LineRange(lineNumber, 1) + MergeEditorLineRange.fromLength(lineNumber, 1), + MergeEditorLineRange.fromLength(lineNumber, 1) ); } if (lastBefore.inputRange.contains(lineNumber)) { return lastBefore; } - const containingRange = new LineRange(lineNumber, 1); - const mappedRange = new LineRange( + const containingRange = MergeEditorLineRange.fromLength(lineNumber, 1); + const mappedRange = MergeEditorLineRange.fromLength( lineNumber + lastBefore.outputRange.endLineNumberExclusive - lastBefore.inputRange.endLineNumberExclusive, @@ -166,7 +166,7 @@ export class MappingAlignment { const alignments = new Array>(); - function pushAndReset(inputRange: LineRange) { + function pushAndReset(inputRange: MergeEditorLineRange) { const mapping1 = LineRangeMapping.join(currentDiffs[0]) || new LineRangeMapping(inputRange, inputRange.delta(deltaFromBaseToInput[0])); const mapping2 = LineRangeMapping.join(currentDiffs[1]) || new LineRangeMapping(inputRange, inputRange.delta(deltaFromBaseToInput[1])); @@ -183,11 +183,11 @@ export class MappingAlignment { currentDiffs[1] = []; } - let currentInputRange: LineRange | undefined; + let currentInputRange: MergeEditorLineRange | undefined; for (const diff of combinedDiffs) { const range = diff.diff.inputRange; - if (currentInputRange && !currentInputRange.touches(range)) { + if (currentInputRange && !currentInputRange.intersectsOrTouches(range)) { pushAndReset(currentInputRange); currentInputRange = undefined; } @@ -204,10 +204,10 @@ export class MappingAlignment { } constructor( - public readonly inputRange: LineRange, - public readonly output1Range: LineRange, + public readonly inputRange: MergeEditorLineRange, + public readonly output1Range: MergeEditorLineRange, public readonly output1LineMappings: T[], - public readonly output2Range: LineRange, + public readonly output2Range: MergeEditorLineRange, public readonly output2LineMappings: T[], ) { } @@ -228,15 +228,15 @@ export class DetailedLineRangeMapping extends LineRangeMapping { public readonly rangeMappings: readonly RangeMapping[]; constructor( - inputRange: LineRange, + inputRange: MergeEditorLineRange, public readonly inputTextModel: ITextModel, - outputRange: LineRange, + outputRange: MergeEditorLineRange, public readonly outputTextModel: ITextModel, rangeMappings?: readonly RangeMapping[], ) { super(inputRange, outputRange); - this.rangeMappings = rangeMappings || [new RangeMapping(this.inputRange.toRange(), this.outputRange.toRange())]; + this.rangeMappings = rangeMappings || [new RangeMapping(this.inputRange.toExclusiveRange(), this.outputRange.toExclusiveRange())]; } public override addOutputLineDelta(delta: number): DetailedLineRangeMapping { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index 8885b623ea1..30d2ad96b65 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -14,7 +14,7 @@ import { localize } from '../../../../../nls.js'; import { IResourceUndoRedoElement, IUndoRedoService, UndoRedoElementType, UndoRedoGroup } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { EditorModel } from '../../../../common/editor/editorModel.js'; import { IMergeDiffComputer } from './diffComputer.js'; -import { LineRange } from './lineRange.js'; +import { MergeEditorLineRange } from './lineRange.js'; import { DetailedLineRangeMapping, DocumentLineRangeMap, DocumentRangeMap, LineRangeMapping } from './mapping.js'; import { TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from './textModelDiffs.js'; import { MergeEditorTelemetry } from '../telemetry.js'; @@ -170,7 +170,7 @@ export class MergeEditorModel extends EditorModel { const input2Lines = this.input2.textModel.getLinesContent(); const resultLines: string[] = []; - function appendLinesToResult(source: string[], lineRange: LineRange) { + function appendLinesToResult(source: string[], lineRange: MergeEditorLineRange) { for (let i = lineRange.startLineNumber; i < lineRange.endLineNumberExclusive; i++) { resultLines.push(source[i - 1]); } @@ -179,7 +179,7 @@ export class MergeEditorModel extends EditorModel { let baseStartLineNumber = 1; for (const baseRange of baseRanges) { - appendLinesToResult(baseLines, LineRange.fromLineNumbers(baseStartLineNumber, baseRange.baseRange.startLineNumber)); + appendLinesToResult(baseLines, MergeEditorLineRange.fromLineNumbers(baseStartLineNumber, baseRange.baseRange.startLineNumber)); baseStartLineNumber = baseRange.baseRange.endLineNumberExclusive; if (baseRange.input1Diffs.length === 0) { @@ -193,7 +193,7 @@ export class MergeEditorModel extends EditorModel { } } - appendLinesToResult(baseLines, LineRange.fromLineNumbers(baseStartLineNumber, baseLines.length + 1)); + appendLinesToResult(baseLines, MergeEditorLineRange.fromLineNumbers(baseStartLineNumber, baseLines.length + 1)); return resultLines.join(this.resultTextModel.getEOL()); } @@ -273,7 +273,7 @@ export class MergeEditorModel extends EditorModel { return map.projectRange(range).outputRange; } - public getLineRangeInResult(baseRange: LineRange, reader?: IReader): LineRange { + public getLineRangeInResult(baseRange: MergeEditorLineRange, reader?: IReader): MergeEditorLineRange { return this.resultTextModelDiffs.getResultLineRange(baseRange, reader); } @@ -287,9 +287,9 @@ export class MergeEditorModel extends EditorModel { return map.projectRange(range).outputRange; } - public findModifiedBaseRangesInRange(rangeInBase: LineRange): ModifiedBaseRange[] { + public findModifiedBaseRangesInRange(rangeInBase: MergeEditorLineRange): ModifiedBaseRange[] { // TODO use binary search - return this.modifiedBaseRanges.get().filter(r => r.baseRange.intersects(rangeInBase)); + return this.modifiedBaseRanges.get().filter(r => r.baseRange.intersectsOrTouches(rangeInBase)); } public readonly diffComputingState = derived(this, reader => { @@ -333,9 +333,9 @@ export class MergeEditorModel extends EditorModel { states, resultDiffs, (baseRange, diff) => - baseRange[0].baseRange.touches(diff.inputRange) + baseRange[0].baseRange.intersectsOrTouches(diff.inputRange) ? CompareResult.neitherLessOrGreaterThan - : LineRange.compareByStart( + : MergeEditorLineRange.compareByStart( baseRange[0].baseRange, diff.inputRange ) @@ -590,7 +590,7 @@ export class MergeEditorModel extends EditorModel { const states = this.modifiedBaseRangeResultStates.get(); const outputLines: string[] = []; - function appendLinesToResult(source: string[], lineRange: LineRange) { + function appendLinesToResult(source: string[], lineRange: MergeEditorLineRange) { for (let i = lineRange.startLineNumber; i < lineRange.endLineNumberExclusive; i++) { outputLines.push(source[i - 1]); } @@ -604,7 +604,7 @@ export class MergeEditorModel extends EditorModel { } const resultRange = this.resultTextModelDiffs.getResultLineRange(range.baseRange); - appendLinesToResult(resultLines, LineRange.fromLineNumbers(resultStartLineNumber, Math.max(resultStartLineNumber, resultRange.startLineNumber))); + appendLinesToResult(resultLines, MergeEditorLineRange.fromLineNumbers(resultStartLineNumber, Math.max(resultStartLineNumber, resultRange.startLineNumber))); resultStartLineNumber = resultRange.endLineNumberExclusive; outputLines.push('<<<<<<<'); @@ -619,7 +619,7 @@ export class MergeEditorModel extends EditorModel { outputLines.push('>>>>>>>'); } - appendLinesToResult(resultLines, LineRange.fromLineNumbers(resultStartLineNumber, resultLines.length + 1)); + appendLinesToResult(resultLines, MergeEditorLineRange.fromLineNumbers(resultStartLineNumber, resultLines.length + 1)); return outputLines.join('\n'); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts index 3a4bd9618e9..d223dc3fd09 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts @@ -11,7 +11,7 @@ import { Position } from '../../../../../editor/common/core/position.js'; import { Range } from '../../../../../editor/common/core/range.js'; import { ITextModel } from '../../../../../editor/common/model.js'; import { LineRangeEdit, RangeEdit } from './editing.js'; -import { LineRange } from './lineRange.js'; +import { MergeEditorLineRange } from './lineRange.js'; import { DetailedLineRangeMapping, MappingAlignment } from './mapping.js'; /** @@ -49,16 +49,16 @@ export class ModifiedBaseRange { public readonly isEqualChange = equals(this.input1Diffs, this.input2Diffs, (a, b) => a.getLineEdit().equals(b.getLineEdit())); constructor( - public readonly baseRange: LineRange, + public readonly baseRange: MergeEditorLineRange, public readonly baseTextModel: ITextModel, - public readonly input1Range: LineRange, + public readonly input1Range: MergeEditorLineRange, public readonly input1TextModel: ITextModel, /** * From base to input1 */ public readonly input1Diffs: readonly DetailedLineRangeMapping[], - public readonly input2Range: LineRange, + public readonly input2Range: MergeEditorLineRange, public readonly input2TextModel: ITextModel, /** @@ -71,7 +71,7 @@ export class ModifiedBaseRange { } } - public getInputRange(inputNumber: 1 | 2): LineRange { + public getInputRange(inputNumber: 1 | 2): MergeEditorLineRange { return inputNumber === 1 ? this.input1Range : this.input2Range; } @@ -199,7 +199,7 @@ export class ModifiedBaseRange { } } -function editsToLineRangeEdit(range: LineRange, sortedEdits: RangeEdit[], textModel: ITextModel): LineRangeEdit | undefined { +function editsToLineRangeEdit(range: MergeEditorLineRange, sortedEdits: RangeEdit[], textModel: ITextModel): LineRangeEdit | undefined { let text = ''; const startsLineBefore = range.startLineNumber > 1; let currentPosition = startsLineBefore diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts index 2741b6681d4..2bd6be05fdc 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts @@ -9,7 +9,7 @@ import { Disposable, toDisposable } from '../../../../../base/common/lifecycle.j import { ITextModel } from '../../../../../editor/common/model.js'; import { DetailedLineRangeMapping } from './mapping.js'; import { LineRangeEdit } from './editing.js'; -import { LineRange } from './lineRange.js'; +import { MergeEditorLineRange } from './lineRange.js'; import { ReentrancyBarrier } from '../../../../../base/common/controlFlow.js'; import { IMergeDiffComputer } from './diffComputer.js'; import { autorun, IObservableWithChange, IReader, ITransaction, observableSignal, observableValue, transaction } from '../../../../../base/common/observable.js'; @@ -145,7 +145,7 @@ export class TextModelDiffs extends Disposable { diffs = diffs.map((d) => d.outputRange.isAfter(diffToRemove.outputRange) - ? d.addOutputLineDelta(diffToRemove.inputRange.lineCount - diffToRemove.outputRange.lineCount) + ? d.addOutputLineDelta(diffToRemove.inputRange.length - diffToRemove.outputRange.length) : d ); } @@ -162,7 +162,7 @@ export class TextModelDiffs extends Disposable { const editMapping = new DetailedLineRangeMapping( edit.range, this.baseTextModel, - new LineRange(edit.range.startLineNumber, edit.newLines.length), + MergeEditorLineRange.fromLength(edit.range.startLineNumber, edit.newLines.length), this.textModel ); @@ -170,7 +170,7 @@ export class TextModelDiffs extends Disposable { let delta = 0; const newDiffs = new Array(); for (const diff of this.diffs.get()) { - if (diff.inputRange.touches(edit.range)) { + if (diff.inputRange.intersectsOrTouches(edit.range)) { throw new BugIndicatingError('Edit must be conflict free.'); } else if (diff.inputRange.isAfter(edit.range)) { if (!firstAfter) { @@ -178,13 +178,13 @@ export class TextModelDiffs extends Disposable { newDiffs.push(editMapping.addOutputLineDelta(delta)); } - newDiffs.push(diff.addOutputLineDelta(edit.newLines.length - edit.range.lineCount)); + newDiffs.push(diff.addOutputLineDelta(edit.newLines.length - edit.range.length)); } else { newDiffs.push(diff); } if (!firstAfter) { - delta += diff.outputRange.lineCount - diff.inputRange.lineCount; + delta += diff.outputRange.length - diff.inputRange.length; } } @@ -200,8 +200,8 @@ export class TextModelDiffs extends Disposable { this._diffs.set(newDiffs, transaction, TextModelDiffChangeReason.other); } - public findTouchingDiffs(baseRange: LineRange): DetailedLineRangeMapping[] { - return this.diffs.get().filter(d => d.inputRange.touches(baseRange)); + public findTouchingDiffs(baseRange: MergeEditorLineRange): DetailedLineRangeMapping[] { + return this.diffs.get().filter(d => d.inputRange.intersectsOrTouches(baseRange)); } private getResultLine(lineNumber: number, reader?: IReader): number | DetailedLineRangeMapping { @@ -219,7 +219,7 @@ export class TextModelDiffs extends Disposable { return lineNumber + offset; } - public getResultLineRange(baseRange: LineRange, reader?: IReader): LineRange { + public getResultLineRange(baseRange: MergeEditorLineRange, reader?: IReader): MergeEditorLineRange { let start = this.getResultLine(baseRange.startLineNumber, reader); if (typeof start !== 'number') { start = start.outputRange.startLineNumber; @@ -229,7 +229,7 @@ export class TextModelDiffs extends Disposable { endExclusive = endExclusive.outputRange.endLineNumberExclusive; } - return LineRange.fromLineNumbers(start, endExclusive); + return MergeEditorLineRange.fromLineNumbers(start, endExclusive); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts index 18cb85e2292..b2bea7169cb 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts @@ -7,7 +7,7 @@ import { h, reset } from '../../../../../base/browser/dom.js'; import { Disposable, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun, IReader, observableFromEvent, observableSignal, observableSignalFromEvent, transaction } from '../../../../../base/common/observable.js'; import { CodeEditorWidget } from '../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { LineRange } from '../model/lineRange.js'; +import { MergeEditorLineRange } from '../model/lineRange.js'; export class EditorGutter extends Disposable { private readonly scrollTop = observableFromEvent(this, @@ -78,7 +78,7 @@ export class EditorGutter extends D if (visibleRanges.length > 0) { const visibleRange = visibleRanges[0]; - const visibleRange2 = new LineRange( + const visibleRange2 = MergeEditorLineRange.fromLength( visibleRange.startLineNumber, visibleRange.endLineNumber - visibleRange.startLineNumber ).deltaEnd(1); @@ -89,7 +89,7 @@ export class EditorGutter extends D ); for (const gutterItem of gutterItems) { - if (!gutterItem.range.touches(visibleRange2)) { + if (!gutterItem.range.intersectsOrTouches(visibleRange2)) { continue; } @@ -140,14 +140,14 @@ class ManagedGutterItemView { } export interface IGutterItemProvider { - getIntersectingGutterItems(range: LineRange, reader: IReader): TItem[]; + getIntersectingGutterItems(range: MergeEditorLineRange, reader: IReader): TItem[]; createView(item: TItem, target: HTMLElement): IGutterItemView; } export interface IGutterItemInfo { id: string; - range: LineRange; + range: MergeEditorLineRange; } export interface IGutterItemView extends IDisposable { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts index c5730805fd3..4a94bc83b82 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts @@ -17,7 +17,7 @@ import { IConfigurationService } from '../../../../../../platform/configuration/ import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../../platform/label/common/label.js'; -import { LineRange } from '../../model/lineRange.js'; +import { MergeEditorLineRange } from '../../model/lineRange.js'; import { applyObservableDecorations, join } from '../../utils.js'; import { handledConflictMinimapOverViewRulerColor, unhandledConflictMinimapOverViewRulerColor } from '../colors.js'; import { EditorGutter } from '../editorGutter.js'; @@ -141,9 +141,9 @@ export class ResultCodeEditorView extends CodeEditorView { const baseRangeWithStoreAndTouchingDiffs = join( model.modifiedBaseRanges.read(reader), model.baseResultDiffs.read(reader), - (baseRange, diff) => baseRange.baseRange.touches(diff.inputRange) + (baseRange, diff) => baseRange.baseRange.intersectsOrTouches(diff.inputRange) ? CompareResult.neitherLessOrGreaterThan - : LineRange.compareByStart( + : MergeEditorLineRange.compareByStart( baseRange.baseRange, diff.inputRange ) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts index e607effbacd..caf33b64b2b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts @@ -16,8 +16,8 @@ import { addLength, lengthBetweenPositions, lengthOfRange } from '../model/range export type LineAlignment = [input1LineNumber: number | undefined, baseLineNumber: number, input2LineNumber: number | undefined]; export function getAlignments(m: ModifiedBaseRange): LineAlignment[] { - const equalRanges1 = toEqualRangeMappings(m.input1Diffs.flatMap(d => d.rangeMappings), m.baseRange.toRange(), m.input1Range.toRange()); - const equalRanges2 = toEqualRangeMappings(m.input2Diffs.flatMap(d => d.rangeMappings), m.baseRange.toRange(), m.input2Range.toRange()); + const equalRanges1 = toEqualRangeMappings(m.input1Diffs.flatMap(d => d.rangeMappings), m.baseRange.toExclusiveRange(), m.input1Range.toExclusiveRange()); + const equalRanges2 = toEqualRangeMappings(m.input2Diffs.flatMap(d => d.rangeMappings), m.baseRange.toExclusiveRange(), m.input2Range.toExclusiveRange()); const commonRanges = splitUpCommonEqualRangeMappings(equalRanges1, equalRanges2); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts index b8158dc995a..8417427fbfc 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts @@ -12,7 +12,7 @@ import { ITextModel } from '../../../../../editor/common/model.js'; import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { INotificationService } from '../../../../../platform/notification/common/notification.js'; -import { LineRange } from '../model/lineRange.js'; +import { MergeEditorLineRange } from '../model/lineRange.js'; import { MergeEditorModel } from '../model/mergeEditorModel.js'; import { InputNumber, ModifiedBaseRange, ModifiedBaseRangeState } from '../model/modifiedBaseRange.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; @@ -49,7 +49,7 @@ export class MergeEditorViewModel extends Disposable { for (const change of e.changes) { const rangeInBase = this.model.translateResultRangeToBase(Range.lift(change.range)); - const baseRanges = this.model.findModifiedBaseRangesInRange(new LineRange(rangeInBase.startLineNumber, rangeInBase.endLineNumber - rangeInBase.startLineNumber)); + const baseRanges = this.model.findModifiedBaseRangesInRange(MergeEditorLineRange.fromLength(rangeInBase.startLineNumber, rangeInBase.endLineNumber - rangeInBase.startLineNumber)); if (baseRanges.length === 1) { const isHandled = this.model.isHandled(baseRanges[0]).get(); if (!isHandled) { @@ -166,7 +166,7 @@ export class MergeEditorViewModel extends Disposable { }; }); - private getRangeOfModifiedBaseRange(editor: CodeEditorView, modifiedBaseRange: ModifiedBaseRange, reader: IReader | undefined): LineRange { + private getRangeOfModifiedBaseRange(editor: CodeEditorView, modifiedBaseRange: ModifiedBaseRange, reader: IReader | undefined): MergeEditorLineRange { if (editor === this.resultCodeEditorView) { return this.model.getLineRangeInResult(modifiedBaseRange.baseRange, reader); } else if (editor === this.baseCodeEditorView.get()) { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts index edd45916373..6590791eac2 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts @@ -8,7 +8,7 @@ import { CompareResult } from '../../../../../base/common/arrays.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { IObservable, IReader } from '../../../../../base/common/observable.js'; import { ICodeEditor, IViewZoneChangeAccessor } from '../../../../../editor/browser/editorBrowser.js'; -import { LineRange } from '../model/lineRange.js'; +import { MergeEditorLineRange } from '../model/lineRange.js'; import { DetailedLineRangeMapping } from '../model/mapping.js'; import { ModifiedBaseRange } from '../model/modifiedBaseRange.js'; import { join } from '../utils.js'; @@ -54,9 +54,9 @@ export class ViewZoneComputer { model.modifiedBaseRanges.read(reader), resultDiffs, (baseRange, diff) => - baseRange.baseRange.touches(diff.inputRange) + baseRange.baseRange.intersectsOrTouches(diff.inputRange) ? CompareResult.neitherLessOrGreaterThan - : LineRange.compareByStart( + : MergeEditorLineRange.compareByStart( baseRange.baseRange, diff.inputRange ) diff --git a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts index 65b7b366c39..da8ef8bb060 100644 --- a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts +++ b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts @@ -343,7 +343,7 @@ class MergeModelInterface extends Disposable { applyRanges( baseTextModel, baseRanges.map((r, idx) => ({ - range: r.baseRange.toRange(), + range: r.baseRange.toExclusiveRange(), label: toSmallNumbersDec(idx), })) ); @@ -352,7 +352,7 @@ class MergeModelInterface extends Disposable { applyRanges( input1TextModel, baseRanges.map((r, idx) => ({ - range: r.input1Range.toRange(), + range: r.input1Range.toExclusiveRange(), label: toSmallNumbersDec(idx), })) ); @@ -361,7 +361,7 @@ class MergeModelInterface extends Disposable { applyRanges( input2TextModel, baseRanges.map((r, idx) => ({ - range: r.input2Range.toRange(), + range: r.input2Range.toExclusiveRange(), label: toSmallNumbersDec(idx), })) ); @@ -370,7 +370,7 @@ class MergeModelInterface extends Disposable { applyRanges( resultTextModel, baseRanges.map((r, idx) => ({ - range: this.mergeModel.getLineRangeInResult(r.baseRange).toRange(), + range: this.mergeModel.getLineRangeInResult(r.baseRange).toExclusiveRange(), label: `{${this.mergeModel.getState(r).get()}}${toSmallNumbersDec(idx)}`, })) ); diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts index 390c8b44ff3..5937359f548 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts @@ -147,7 +147,7 @@ class QuickDiffDecorator extends Disposable { continue; } - if (quickDiff.kind !== 'primary' && primaryQuickDiffChanges.some(c => c.change2.modified.overlapOrTouch(change.change2.modified))) { + if (quickDiff.kind !== 'primary' && primaryQuickDiffChanges.some(c => c.change2.modified.intersectsOrTouches(change.change2.modified))) { // Overlap with primary quick diff changes continue; } diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index 6d763793ebc..1d16201b68f 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -339,7 +339,7 @@ class QuickDiffWidget extends PeekViewWidget { const change = this.model.changes[this._index]; const quickDiffsWithChange = this.model.changes - .filter(c => change.change2.modified.overlapOrTouch(c.change2.modified)) + .filter(c => change.change2.modified.intersectsOrTouches(c.change2.modified)) .map(c => c.providerId); return this.model.quickDiffs From 8067943119d34827d9674dcb0e7e9427b7cc057c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 7 May 2025 10:24:23 -0700 Subject: [PATCH 90/90] Remove a few deprecated settings for js/ts These settings have been marked deprecated for a while --- .../typescript-language-features/package.json | 41 ------------------- .../package.nls.json | 5 --- .../src/configuration/configuration.ts | 6 +-- .../fileConfigurationManager.ts | 2 +- 4 files changed, 3 insertions(+), 51 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 5f39dc5f984..f6d50b16994 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -278,26 +278,12 @@ ], "scope": "window" }, - "javascript.implicitProjectConfig.checkJs": { - "type": "boolean", - "default": false, - "markdownDescription": "%configuration.implicitProjectConfig.checkJs%", - "markdownDeprecationMessage": "%configuration.javascript.checkJs.checkJs.deprecation%", - "scope": "window" - }, "js/ts.implicitProjectConfig.checkJs": { "type": "boolean", "default": false, "markdownDescription": "%configuration.implicitProjectConfig.checkJs%", "scope": "window" }, - "javascript.implicitProjectConfig.experimentalDecorators": { - "type": "boolean", - "default": false, - "markdownDescription": "%configuration.implicitProjectConfig.experimentalDecorators%", - "markdownDeprecationMessage": "%configuration.javascript.checkJs.experimentalDecorators.deprecation%", - "scope": "window" - }, "js/ts.implicitProjectConfig.experimentalDecorators": { "type": "boolean", "default": false, @@ -817,20 +803,6 @@ "markdownDescription": "%typescript.preferences.preferTypeOnlyAutoImports%", "scope": "resource" }, - "javascript.preferences.renameShorthandProperties": { - "type": "boolean", - "default": true, - "description": "%typescript.preferences.useAliasesForRenames%", - "deprecationMessage": "%typescript.preferences.renameShorthandProperties.deprecationMessage%", - "scope": "language-overridable" - }, - "typescript.preferences.renameShorthandProperties": { - "type": "boolean", - "default": true, - "description": "%typescript.preferences.useAliasesForRenames%", - "deprecationMessage": "%typescript.preferences.renameShorthandProperties.deprecationMessage%", - "scope": "language-overridable" - }, "javascript.preferences.useAliasesForRenames": { "type": "boolean", "default": true, @@ -1418,13 +1390,6 @@ "description": "%configuration.tsserver.web.typeAcquisition.enabled%", "scope": "window" }, - "typescript.tsserver.useSeparateSyntaxServer": { - "type": "boolean", - "default": true, - "description": "%configuration.tsserver.useSeparateSyntaxServer%", - "markdownDeprecationMessage": "%configuration.tsserver.useSeparateSyntaxServer.deprecation%", - "scope": "window" - }, "typescript.tsserver.useSyntaxServer": { "type": "string", "scope": "window", @@ -1456,12 +1421,6 @@ "experimental" ] }, - "typescript.tsserver.experimental.useVsCodeWatcher": { - "type": "boolean", - "description": "%configuration.tsserver.useVsCodeWatcher%", - "deprecationMessage": "%configuration.tsserver.useVsCodeWatcher.deprecation%", - "default": true - }, "typescript.tsserver.watchOptions": { "description": "%configuration.tsserver.watchOptions%", "scope": "window", diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 44fa54efbcd..e5cdf74ba4f 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -72,7 +72,6 @@ "typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)", "configuration.suggest.paths": "Enable/disable suggestions for paths in import statements and require calls.", "configuration.tsserver.useSeparateSyntaxServer": "Enable/disable spawning a separate TypeScript server that can more quickly respond to syntax related operations, such as calculating folding or computing document symbols.", - "configuration.tsserver.useSeparateSyntaxServer.deprecation": "This setting has been deprecated in favor of `typescript.tsserver.useSyntaxServer`.", "configuration.tsserver.useSyntaxServer": "Controls if TypeScript launches a dedicated server to more quickly handle syntax related operations, such as computing code folding.", "configuration.tsserver.useSyntaxServer.always": "Use a lighter weight syntax server to handle all IntelliSense operations. This syntax server can only provide IntelliSense for opened files.", "configuration.tsserver.useSyntaxServer.never": "Don't use a dedicated syntax server. Use a single server to handle all IntelliSense operations.", @@ -84,9 +83,7 @@ "configuration.implicitProjectConfig.module": "Sets the module system for the program. See more: https://www.typescriptlang.org/tsconfig#module.", "configuration.implicitProjectConfig.target": "Set target JavaScript language version for emitted JavaScript and include library declarations. See more: https://www.typescriptlang.org/tsconfig#target.", "configuration.implicitProjectConfig.checkJs": "Enable/disable semantic checking of JavaScript files. Existing `jsconfig.json` or `tsconfig.json` files override this setting.", - "configuration.javascript.checkJs.checkJs.deprecation": "This setting has been deprecated in favor of `js/ts.implicitProjectConfig.checkJs`.", "configuration.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` in JavaScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.", - "configuration.javascript.checkJs.experimentalDecorators.deprecation": "This setting has been deprecated in favor of `js/ts.implicitProjectConfig.experimentalDecorators`.", "configuration.implicitProjectConfig.strictNullChecks": "Enable/disable [strict null checks](https://www.typescriptlang.org/tsconfig#strictNullChecks) in JavaScript and TypeScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.", "configuration.implicitProjectConfig.strictFunctionTypes": "Enable/disable [strict function types](https://www.typescriptlang.org/tsconfig#strictFunctionTypes) in JavaScript and TypeScript files that are not part of a project. Existing `jsconfig.json` or `tsconfig.json` files override this setting.", "configuration.suggest.jsdoc.generateReturns": "Enable/disable generating `@returns` annotations for JSDoc templates.", @@ -172,7 +169,6 @@ "typescript.suggest.enabled": "Enable/disable autocomplete suggestions.", "configuration.suggest.completeJSDocs": "Enable/disable suggestion to complete JSDoc comments.", "configuration.tsserver.useVsCodeWatcher": "Use VS Code's file watchers instead of TypeScript's. Requires using TypeScript 5.4+ in the workspace.", - "configuration.tsserver.useVsCodeWatcher.deprecation": "Please use the `#typescript.tsserver.watchOptions#` setting instead.", "configuration.tsserver.watchOptions": "Configure which watching strategies should be used to keep track of files and directories.", "configuration.tsserver.watchOptions.vscode": "Use VS Code's file watchers instead of TypeScript's. Requires using TypeScript 5.4+ in the workspace.", "configuration.tsserver.watchOptions.watchFile": "Strategy for how individual files are watched.", @@ -192,7 +188,6 @@ "configuration.tsserver.watchOptions.fallbackPolling.priorityPollingInterval": "Check every file for changes several times a second, but use heuristics to check certain types of files less frequently than others.", "configuration.tsserver.watchOptions.fallbackPolling.dynamicPriorityPolling ": "Use a dynamic queue where less-frequently modified files will be checked less often.", "configuration.tsserver.watchOptions.synchronousWatchDirectory": "Disable deferred watching on directories. Deferred watching is useful when lots of file changes might occur at once (e.g. a change in node_modules from running npm install), but you might want to disable it with this flag for some less-common setups.", - "typescript.preferences.renameShorthandProperties.deprecationMessage": "The setting 'typescript.preferences.renameShorthandProperties' has been deprecated in favor of 'typescript.preferences.useAliasesForRenames'", "typescript.preferences.useAliasesForRenames": "Enable/disable introducing aliases for object shorthand properties during renames.", "typescript.preferences.renameMatchingJsxTags": "When on a JSX tag, try to rename the matching tag instead of renaming the symbol. Requires using TypeScript 5.1+ in the workspace.", "typescript.preferences.organizeImports": "Advanced preferences that control how imports are ordered.", diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index 693e7ad17e4..6a0a2e78beb 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -82,13 +82,11 @@ export class ImplicitProjectConfiguration { } private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('js/ts.implicitProjectConfig.checkJs') - ?? configuration.get('javascript.implicitProjectConfig.checkJs', false); + return configuration.get('js/ts.implicitProjectConfig.checkJs', false); } private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('js/ts.implicitProjectConfig.experimentalDecorators') - ?? configuration.get('javascript.implicitProjectConfig.experimentalDecorators', false); + return configuration.get('js/ts.implicitProjectConfig.experimentalDecorators', false); } private static readImplicitStrictNullChecks(configuration: vscode.WorkspaceConfiguration): boolean { diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index 9b0c10a48b7..39c95402bce 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -188,7 +188,7 @@ export default class FileConfigurationManager extends Disposable { importModuleSpecifierEnding: getImportModuleSpecifierEndingPreference(preferencesConfig), jsxAttributeCompletionStyle: getJsxAttributeCompletionStyle(preferencesConfig), allowTextChangesInNewFiles: document.uri.scheme === fileSchemes.file, - providePrefixAndSuffixTextForRename: preferencesConfig.get('renameShorthandProperties', true) === false ? false : preferencesConfig.get('useAliasesForRenames', true), + providePrefixAndSuffixTextForRename: preferencesConfig.get('useAliasesForRenames', true), allowRenameOfImportPath: true, includeAutomaticOptionalChainCompletions: config.get('suggest.includeAutomaticOptionalChainCompletions', true), provideRefactorNotApplicableReason: true,