From 91990b4e6919ce44efdc7dd309c098cc94eae5c9 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 9 Jan 2025 21:37:35 -0800 Subject: [PATCH 1/6] Fix occasional streaming pauses (#237615) --- src/vs/workbench/contrib/chat/common/chatModel.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 2b23c26cffd65..5c0530f7aa32f 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -352,7 +352,9 @@ export class Response extends Disposable implements IResponse { // The last part can't be merged with- not markdown, or markdown with different permissions this._responseParts.push(progress); } else { - lastResponsePart.content = appendMarkdownString(lastResponsePart.content, progress.content); + // Don't modify the current object, since it's being diffed by the renderer + const idx = this._responseParts.indexOf(lastResponsePart); + this._responseParts[idx] = { ...lastResponsePart, content: appendMarkdownString(lastResponsePart.content, progress.content) }; } this._updateRepr(quiet); } else if (progress.kind === 'textEdit') { From 8e81ea645d41e683ccae3dd454c70670c1a009cb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 10 Jan 2025 06:43:21 +0100 Subject: [PATCH 2/6] watcher - defer `realpath` to only when needed (#237600) --- .../node/watcher/nodejs/nodejsWatcherLib.ts | 73 +++++++++---------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts index 35ea5eea71f6f..b016dde0a9ffd 100644 --- a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts +++ b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts @@ -17,6 +17,7 @@ import { realpath } from '../../../../../base/node/extpath.js'; import { Promises } from '../../../../../base/node/pfs.js'; import { FileChangeType, IFileChange } from '../../../common/files.js'; import { ILogMessage, coalesceEvents, INonRecursiveWatchRequest, parseWatcherPatterns, IRecursiveWatcherWithSubscribe, isFiltered, isWatchRequestWithCorrelation } from '../../../common/watcher.js'; +import { Lazy } from '../../../../../base/common/lazy.js'; export class NodeJSFileWatcherLibrary extends Disposable { @@ -55,6 +56,28 @@ export class NodeJSFileWatcherLibrary extends Disposable { private readonly cts = new CancellationTokenSource(); + private readonly realPath = new Lazy(async () => { + + // This property is intentionally `Lazy` and not using `realcase()` as the counterpart + // in the recursive watcher because of the amount of paths this watcher is dealing with. + // We try as much as possible to avoid even needing `realpath()` if we can because even + // that method does an `lstat()` per segment of the path. + + let result = this.request.path; + + try { + result = await realpath(this.request.path); + + if (this.request.path !== result) { + this.trace(`correcting a path to watch that seems to be a symbolic link (original: ${this.request.path}, real: ${result})`); + } + } catch (error) { + // ignore + } + + return result; + }); + readonly ready = this.watch(); private _isReusingRecursiveWatcher = false; @@ -76,19 +99,13 @@ export class NodeJSFileWatcherLibrary extends Disposable { private async watch(): Promise { try { - const realPath = await this.normalizePath(this.request); + const stat = await promises.stat(this.request.path); if (this.cts.token.isCancellationRequested) { return; } - const stat = await promises.stat(realPath); - - if (this.cts.token.isCancellationRequested) { - return; - } - - this._register(await this.doWatch(realPath, stat.isDirectory())); + this._register(await this.doWatch(stat.isDirectory())); } catch (error) { if (error.code !== 'ENOENT') { this.error(error); @@ -106,46 +123,21 @@ export class NodeJSFileWatcherLibrary extends Disposable { this.onDidWatchFail?.(); } - private async normalizePath(request: INonRecursiveWatchRequest): Promise { - let realPath = request.path; - - try { - - // Check for symbolic link - realPath = await realpath(request.path); - - // Note: we used to also call `realcase()` here, but - // that operation is very expensive for large amounts - // of files and is actually not needed for single - // file/folder watching where we report on the original - // path anyway. - // (https://github.com/microsoft/vscode/issues/237351) - - if (request.path !== realPath) { - this.trace(`correcting a path to watch that seems to be a symbolic link (original: ${request.path}, real: ${realPath})`); - } - } catch (error) { - // ignore - } - - return realPath; - } - - private async doWatch(realPath: string, isDirectory: boolean): Promise { + private async doWatch(isDirectory: boolean): Promise { const disposables = new DisposableStore(); - if (this.doWatchWithExistingWatcher(realPath, isDirectory, disposables)) { + if (this.doWatchWithExistingWatcher(isDirectory, disposables)) { this.trace(`reusing an existing recursive watcher for ${this.request.path}`); this._isReusingRecursiveWatcher = true; } else { this._isReusingRecursiveWatcher = false; - await this.doWatchWithNodeJS(realPath, isDirectory, disposables); + await this.doWatchWithNodeJS(isDirectory, disposables); } return disposables; } - private doWatchWithExistingWatcher(realPath: string, isDirectory: boolean, disposables: DisposableStore): boolean { + private doWatchWithExistingWatcher(isDirectory: boolean, disposables: DisposableStore): boolean { if (isDirectory) { // Recursive watcher re-use is currently not enabled for when // folders are watched. this is because the dispatching in the @@ -162,7 +154,7 @@ export class NodeJSFileWatcherLibrary extends Disposable { } if (error) { - const watchDisposable = await this.doWatch(realPath, isDirectory); + const watchDisposable = await this.doWatch(isDirectory); if (!disposables.isDisposed) { disposables.add(watchDisposable); } else { @@ -188,7 +180,8 @@ export class NodeJSFileWatcherLibrary extends Disposable { return false; } - private async doWatchWithNodeJS(realPath: string, isDirectory: boolean, disposables: DisposableStore): Promise { + private async doWatchWithNodeJS(isDirectory: boolean, disposables: DisposableStore): Promise { + const realPath = await this.realPath.value; // macOS: watching samba shares can crash VSCode so we do // a simple check for the file path pointing to /Volumes @@ -407,7 +400,7 @@ export class NodeJSFileWatcherLibrary extends Disposable { if (fileExists) { this.onFileChange({ resource: requestResource, type: FileChangeType.UPDATED, cId: this.request.correlationId }, true /* skip excludes/includes (file is explicitly watched) */); - watcherDisposables.add(await this.doWatch(realPath, false)); + watcherDisposables.add(await this.doWatch(false)); } // File seems to be really gone, so emit a deleted and failed event From f80816ab8e21c65ed7f1f7e08ccdbffae63610c6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2025 08:41:08 +0100 Subject: [PATCH 3/6] fix leaking disposables (#237586) * chore - mark disposables from registry#register functions as disposables so that they don't appear as leaking * fix various leaking disposables --- src/vs/editor/common/languages.ts | 2 +- .../languageConfigurationRegistry.ts | 10 +++--- .../contrib/codelens/browser/codeLensCache.ts | 2 +- .../contrib/codelens/browser/codelens.ts | 13 +++++--- .../hover/browser/contentHoverRendered.ts | 3 +- .../editor/contrib/links/browser/getLinks.ts | 32 +++++++++++-------- src/vs/monaco.d.ts | 2 +- src/vs/platform/actions/common/actions.ts | 10 +++--- src/vs/platform/commands/common/commands.ts | 4 +-- .../extensions/browser/extensionsList.ts | 4 +-- .../contrib/scm/browser/scmViewPane.ts | 11 +++++-- .../browser/authenticationUsageService.ts | 4 +-- 12 files changed, 57 insertions(+), 40 deletions(-) diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 0a0545a891abe..bef68de579deb 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -2107,7 +2107,7 @@ export interface CodeLens { export interface CodeLensList { lenses: CodeLens[]; - dispose(): void; + dispose?(): void; } export interface CodeLensProvider { diff --git a/src/vs/editor/common/languages/languageConfigurationRegistry.ts b/src/vs/editor/common/languages/languageConfigurationRegistry.ts index bf0516d531b23..f42b06d0b7462 100644 --- a/src/vs/editor/common/languages/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/languages/languageConfigurationRegistry.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from '../../../base/common/event.js'; -import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { Disposable, IDisposable, markAsSingleton, toDisposable } from '../../../base/common/lifecycle.js'; import * as strings from '../../../base/common/strings.js'; import { ITextModel } from '../model.js'; import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from '../core/wordHelper.js'; @@ -202,7 +202,7 @@ class ComposedLanguageConfiguration { ); this._entries.push(entry); this._resolved = null; - return toDisposable(() => { + return markAsSingleton(toDisposable(() => { for (let i = 0; i < this._entries.length; i++) { if (this._entries[i] === entry) { this._entries.splice(i, 1); @@ -210,7 +210,7 @@ class ComposedLanguageConfiguration { break; } } - }); + })); } public getResolvedConfiguration(): ResolvedLanguageConfiguration | null { @@ -332,10 +332,10 @@ export class LanguageConfigurationRegistry extends Disposable { const disposable = entries.register(configuration, priority); this._onDidChange.fire(new LanguageConfigurationChangeEvent(languageId)); - return toDisposable(() => { + return markAsSingleton(toDisposable(() => { disposable.dispose(); this._onDidChange.fire(new LanguageConfigurationChangeEvent(languageId)); - }); + })); } public getLanguageConfiguration(languageId: string): ResolvedLanguageConfiguration | null { diff --git a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts index 5f479695091d1..6dcde99c36995 100644 --- a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts @@ -77,7 +77,7 @@ export class CodeLensCache implements ICodeLensCache { }; }); const copyModel = new CodeLensModel(); - copyModel.add({ lenses: copyItems, dispose: () => { } }, this._fakeProvider); + copyModel.add({ lenses: copyItems }, this._fakeProvider); const item = new CacheItem(model.getLineCount(), copyModel); this._cache.set(model.uri.toString(), item); diff --git a/src/vs/editor/contrib/codelens/browser/codelens.ts b/src/vs/editor/contrib/codelens/browser/codelens.ts index 0a777339b04e8..be24cae08b018 100644 --- a/src/vs/editor/contrib/codelens/browser/codelens.ts +++ b/src/vs/editor/contrib/codelens/browser/codelens.ts @@ -5,7 +5,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { illegalArgument, onUnexpectedExternalError } from '../../../../base/common/errors.js'; -import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, isDisposable } from '../../../../base/common/lifecycle.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { ITextModel } from '../../../common/model.js'; @@ -24,18 +24,21 @@ export class CodeLensModel { lenses: CodeLensItem[] = []; - private readonly _disposables = new DisposableStore(); + private _store: DisposableStore | undefined; dispose(): void { - this._disposables.dispose(); + this._store?.dispose(); } get isDisposed(): boolean { - return this._disposables.isDisposed; + return this._store?.isDisposed ?? false; } add(list: CodeLensList, provider: CodeLensProvider): void { - this._disposables.add(list); + if (isDisposable(list)) { + this._store ??= new DisposableStore(); + this._store.add(list); + } for (const symbol of list.lenses) { this.lenses.push({ symbol, provider }); } diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index 81ccfcbc60b22..a80bdb7966fb0 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -273,6 +273,7 @@ class RenderedContentHoverParts extends Disposable { ...hoverContext }; const disposables = new DisposableStore(); + disposables.add(statusBar); for (const participant of participants) { const renderedHoverParts = this._renderHoverPartsForParticipant(hoverParts, participant, hoverRenderingContext); disposables.add(renderedHoverParts); @@ -294,7 +295,7 @@ class RenderedContentHoverParts extends Disposable { actions: renderedStatusBar.actions, }); } - return toDisposable(() => { disposables.dispose(); }); + return disposables; } private _renderHoverPartsForParticipant(hoverParts: IHoverPart[], participant: IEditorHoverParticipant, hoverRenderingContext: IEditorHoverRenderContext): IRenderedHoverParts { diff --git a/src/vs/editor/contrib/links/browser/getLinks.ts b/src/vs/editor/contrib/links/browser/getLinks.ts index cd2e19c756e06..be8a31af119bf 100644 --- a/src/vs/editor/contrib/links/browser/getLinks.ts +++ b/src/vs/editor/contrib/links/browser/getLinks.ts @@ -70,6 +70,8 @@ export class Link implements ILink { export class LinksList { + static readonly Empty = new LinksList([]); + readonly links: Link[]; private readonly _disposables = new DisposableStore(); @@ -137,27 +139,31 @@ export class LinksList { } -export function getLinks(providers: LanguageFeatureRegistry, model: ITextModel, token: CancellationToken): Promise { - +export async function getLinks(providers: LanguageFeatureRegistry, model: ITextModel, token: CancellationToken): Promise { const lists: [ILinksList, LinkProvider][] = []; // ask all providers for links in parallel - const promises = providers.ordered(model).reverse().map((provider, i) => { - return Promise.resolve(provider.provideLinks(model, token)).then(result => { + const promises = providers.ordered(model).reverse().map(async (provider, i) => { + try { + const result = await provider.provideLinks(model, token); if (result) { lists[i] = [result, provider]; } - }, onUnexpectedExternalError); - }); - - return Promise.all(promises).then(() => { - const result = new LinksList(coalesce(lists)); - if (!token.isCancellationRequested) { - return result; + } catch (err) { + onUnexpectedExternalError(err); } - result.dispose(); - return new LinksList([]); }); + + await Promise.all(promises); + + let res = new LinksList(coalesce(lists)); + + if (token.isCancellationRequested) { + res.dispose(); + res = LinksList.Empty; + } + + return res; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index bd27d47994110..5db429fff414e 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -8060,7 +8060,7 @@ declare namespace monaco.languages { export interface CodeLensList { lenses: CodeLens[]; - dispose(): void; + dispose?(): void; } export interface CodeLensProvider { diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 2b784b5bf5e1e..9f1452f4b65cc 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -5,7 +5,7 @@ import { IAction, SubmenuAction } from '../../../base/common/actions.js'; import { Event, MicrotaskEmitter } from '../../../base/common/event.js'; -import { DisposableStore, dispose, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { DisposableStore, dispose, IDisposable, markAsSingleton, toDisposable } from '../../../base/common/lifecycle.js'; import { LinkedList } from '../../../base/common/linkedList.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { ICommandAction, ICommandActionTitle, Icon, ILocalizedString } from '../../action/common/action.js'; @@ -397,11 +397,11 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { this._commands.set(command.id, command); this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette)); - return toDisposable(() => { + return markAsSingleton(toDisposable(() => { if (this._commands.delete(command.id)) { this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette)); } - }); + })); } getCommand(id: string): ICommandAction | undefined { @@ -422,10 +422,10 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { } const rm = list.push(item); this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id)); - return toDisposable(() => { + return markAsSingleton(toDisposable(() => { rm(); this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id)); - }); + })); } appendMenuItems(items: Iterable<{ id: MenuId; item: IMenuItem | ISubmenuItem }>): IDisposable { diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index 53e32c4842e1b..b6e7bc6fe501f 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { Iterable } from '../../../base/common/iterator.js'; import { IJSONSchema } from '../../../base/common/jsonSchema.js'; -import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { IDisposable, markAsSingleton, toDisposable } from '../../../base/common/lifecycle.js'; import { LinkedList } from '../../../base/common/linkedList.js'; import { TypeConstraint, validateConstraints } from '../../../base/common/types.js'; import { ILocalizedString } from '../../action/common/action.js'; @@ -121,7 +121,7 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR // tell the world about this command this._onDidRegisterCommand.fire(id); - return ret; + return markAsSingleton(ret); } registerCommandAlias(oldId: string, newId: string): IDisposable { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 2afa3e73bbf63..7293bfef2b0a1 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -114,7 +114,7 @@ export class Renderer implements IPagedRenderer { focusOnlyEnabledItems: true }); actionbar.setFocusable(false); - actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); + const actionBarListener = actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); const extensionStatusIconAction = this.instantiationService.createInstance(ExtensionStatusAction); const actions = [ @@ -150,7 +150,7 @@ export class Renderer implements IPagedRenderer { const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]); actionbar.push(actions, { icon: true, label: true }); - const disposable = combinedDisposable(...actions, ...widgets, actionbar, extensionContainers); + const disposable = combinedDisposable(...actions, ...widgets, actionbar, actionBarListener, extensionContainers); return { root, element, icon, name, installCount, ratings, description, publisherDisplayName, disposables: [disposable], actionbar, diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 8a05bb6c06f7e..a786b73aab1a5 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -21,7 +21,7 @@ import { IContextKeyService, IContextKey, ContextKeyExpr, RawContextKey } from ' import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { MenuItemAction, IMenuService, registerAction2, MenuId, IAction2Options, MenuRegistry, Action2, IMenu } from '../../../../platform/actions/common/actions.js'; -import { IAction, ActionRunner, Action, Separator, IActionRunner } from '../../../../base/common/actions.js'; +import { IAction, ActionRunner, Action, Separator, IActionRunner, toAction } from '../../../../base/common/actions.js'; import { ActionBar, IActionViewItemProvider } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IThemeService, IFileIconTheme } from '../../../../platform/theme/common/themeService.js'; import { isSCMResource, isSCMResourceGroup, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider, isSCMActionButton, isSCMViewService, isSCMResourceNode, connectPrimaryMenu } from './util.js'; @@ -2988,7 +2988,14 @@ export class SCMActionButton implements IDisposable { for (let index = 0; index < button.secondaryCommands.length; index++) { const commands = button.secondaryCommands[index]; for (const command of commands) { - actions.push(new Action(command.id, command.title, undefined, true, async () => await this.executeCommand(command.id, ...(command.arguments || [])))); + actions.push(toAction({ + id: command.id, + label: command.title, + enabled: true, + run: async () => { + await this.executeCommand(command.id, ...(command.arguments || [])); + } + })); } if (commands.length) { actions.push(new Separator()); diff --git a/src/vs/workbench/services/authentication/browser/authenticationUsageService.ts b/src/vs/workbench/services/authentication/browser/authenticationUsageService.ts index 514e1ce72da75..08f8436fdf793 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationUsageService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationUsageService.ts @@ -81,11 +81,11 @@ export class AuthenticationUsageService extends Disposable implements IAuthentic } } - this._authenticationService.onDidRegisterAuthenticationProvider( + this._register(this._authenticationService.onDidRegisterAuthenticationProvider( provider => this._queue.queue( () => this._addExtensionsToCache(provider.id) ) - ); + )); } async initializeExtensionUsageCache(): Promise { From e1e2944acf490f5490b3250bf23147ff8a7bb0b3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2025 11:03:18 +0100 Subject: [PATCH 4/6] fixes https://github.com/microsoft/vscode/issues/237171 (#237628) --- src/vs/workbench/contrib/inlineChat/common/inlineChat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index b541cb0e62145..e478a0fdac466 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -140,7 +140,7 @@ export const inlineChatInputBackground = registerColor('inlineChatInput.backgrou export const inlineChatDiffInserted = registerColor('inlineChatDiff.inserted', transparent(diffInserted, .5), localize('inlineChatDiff.inserted', "Background color of inserted text in the interactive editor input")); export const overviewRulerInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.')); -export const minimapInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.')); +export const minimapInlineChatDiffInserted = registerColor('editorMinimap.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorMinimap.inlineChatInserted', 'Minimap marker color for inline chat inserted content.')); export const inlineChatDiffRemoved = registerColor('inlineChatDiff.removed', transparent(diffRemoved, .5), localize('inlineChatDiff.removed', "Background color of removed text in the interactive editor input")); export const overviewRulerInlineChatDiffRemoved = registerColor('editorOverviewRuler.inlineChatRemoved', { dark: transparent(diffRemoved, 0.6), light: transparent(diffRemoved, 0.8), hcDark: transparent(diffRemoved, 0.6), hcLight: transparent(diffRemoved, 0.8) }, localize('editorOverviewRuler.inlineChatRemoved', 'Overview ruler marker color for inline chat removed content.')); From 7faf268c1b3f31e33e12c76a62d818a3bf67c689 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 10 Jan 2025 11:07:59 +0100 Subject: [PATCH 5/6] fix https://github.com/microsoft/vscode/issues/235322 (#237630) --- src/vs/base/common/errors.ts | 11 ++++++++++- .../api/browser/mainThreadLanguageModels.ts | 3 ++- .../workbench/api/common/extHostLanguageModels.ts | 2 +- src/vs/workbench/api/common/extHostTypes.ts | 13 +++++++++++-- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index a6930ef51cf5b..888aa3b063021 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -126,9 +126,14 @@ export interface SerializedError { readonly message: string; readonly stack: string; readonly noTelemetry: boolean; + readonly code?: string; readonly cause?: SerializedError; } +type ErrorWithCode = Error & { + code: string | undefined; +}; + export function transformErrorForSerialization(error: Error): SerializedError; export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { @@ -141,7 +146,8 @@ export function transformErrorForSerialization(error: any): any { message, stack, noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error), - cause: cause ? transformErrorForSerialization(cause) : undefined + cause: cause ? transformErrorForSerialization(cause) : undefined, + code: (error).code }; } @@ -159,6 +165,9 @@ export function transformErrorFromSerialization(data: SerializedError): Error { } error.message = data.message; error.stack = data.stack; + if (data.code) { + (error).code = data.code; + } if (data.cause) { error.cause = transformErrorFromSerialization(data.cause); } diff --git a/src/vs/workbench/api/browser/mainThreadLanguageModels.ts b/src/vs/workbench/api/browser/mainThreadLanguageModels.ts index 659f026e78d1b..1b1cb20d72b6b 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageModels.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageModels.ts @@ -20,6 +20,7 @@ import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticati import { IExtHostContext, extHostNamedCustomer } from '../../services/extensions/common/extHostCustomers.js'; import { IExtensionService } from '../../services/extensions/common/extensions.js'; import { ExtHostContext, ExtHostLanguageModelsShape, MainContext, MainThreadLanguageModelsShape } from '../common/extHost.protocol.js'; +import { LanguageModelError } from '../common/extHostTypes.js'; @extHostNamedCustomer(MainContext.MainThreadLanguageModels) export class MainThreadLanguageModels implements MainThreadLanguageModelsShape { @@ -97,7 +98,7 @@ export class MainThreadLanguageModels implements MainThreadLanguageModelsShape { if (data) { this._pendingProgress.delete(requestId); if (err) { - const error = transformErrorFromSerialization(err); + const error = LanguageModelError.tryDeserialize(err) ?? transformErrorFromSerialization(err); data.stream.reject(error); data.defer.error(error); } else { diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index b5267b761c74a..9264a42d2899a 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -434,7 +434,7 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { if (error) { // we error the stream because that's the only way to signal // that the request has failed - data.res.reject(transformErrorFromSerialization(error)); + data.res.reject(extHostTypes.LanguageModelError.tryDeserialize(error) ?? transformErrorFromSerialization(error)); } else { data.res.resolve(); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 667b018aa332a..abb6a3e737b2b 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -7,7 +7,7 @@ import type * as vscode from 'vscode'; import { asArray, coalesceInPlace, equals } from '../../../base/common/arrays.js'; -import { illegalArgument } from '../../../base/common/errors.js'; +import { illegalArgument, SerializedError } from '../../../base/common/errors.js'; import { IRelativePattern } from '../../../base/common/glob.js'; import { MarkdownString as BaseMarkdownString, MarkdownStringTrustedOptions } from '../../../base/common/htmlContent.js'; import { ResourceMap } from '../../../base/common/map.js'; @@ -4847,6 +4847,8 @@ export class LanguageModelChatAssistantMessage { export class LanguageModelError extends Error { + static readonly #name = 'LanguageModelError'; + static NotFound(message?: string): LanguageModelError { return new LanguageModelError(message, LanguageModelError.NotFound.name); } @@ -4859,11 +4861,18 @@ export class LanguageModelError extends Error { return new LanguageModelError(message, LanguageModelError.Blocked.name); } + static tryDeserialize(data: SerializedError): LanguageModelError | undefined { + if (data.name !== LanguageModelError.#name) { + return undefined; + } + return new LanguageModelError(data.message, data.code, data.cause); + } + readonly code: string; constructor(message?: string, code?: string, cause?: Error) { super(message, { cause }); - this.name = 'LanguageModelError'; + this.name = LanguageModelError.#name; this.code = code ?? ''; } From 3073dc1fb489badd5a07f20af503c408780a51f7 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:00:33 +0100 Subject: [PATCH 6/6] padding between statusbar and inline edit (#237632) fixes #11597 --- .../browser/view/inlineEdits/gutterIndicatorView.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts index e0a1ce126d0e2..dd4d8852858db 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -116,7 +116,8 @@ export class InlineEditsGutterIndicator extends Disposable { const layout = this._editorObs.layoutInfo.read(reader); - const fullViewPort = Rect.fromLeftTopRightBottom(0, 0, layout.width, layout.height); + const bottomPadding = 1; + const fullViewPort = Rect.fromLeftTopRightBottom(0, 0, layout.width, layout.height - bottomPadding); const viewPortWithStickyScroll = fullViewPort.withTop(this._stickyScrollHeight.read(reader)); const targetVertRange = s.lineOffsetRange.read(reader);