From 7f96f911c7fc825a070ebb1aa0c2e3e76cf3a9a8 Mon Sep 17 00:00:00 2001 From: BABA <38986298+BABA983@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:06:06 +0800 Subject: [PATCH 001/479] Resolve custom editor with canonical resource --- src/vs/workbench/api/browser/mainThreadCustomEditors.ts | 4 +++- .../contrib/customEditor/browser/customEditors.ts | 6 ++++-- .../customEditor/common/customEditorModelManager.ts | 9 +++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 73c9a19d3d0..e9d0ca29eef 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -44,6 +44,7 @@ import { ResourceWorkingCopy } from '../../services/workingCopy/common/resourceW import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopySaveEvent, NO_TYPE_ID, WorkingCopyCapabilities } from '../../services/workingCopy/common/workingCopy.js'; import { IWorkingCopyFileService, WorkingCopyFileEvent } from '../../services/workingCopy/common/workingCopyFileService.js'; import { IWorkingCopyService } from '../../services/workingCopy/common/workingCopyService.js'; +import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; const enum CustomEditorModelType { Custom, @@ -73,6 +74,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc @IEditorService private readonly _editorService: IEditorService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, + @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService, ) { super(); @@ -197,7 +199,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc } try { - await this._proxyCustomEditors.$resolveCustomEditor(resource, handle, viewType, { + await this._proxyCustomEditors.$resolveCustomEditor(this._uriIdentityService.asCanonicalUri(resource), handle, viewType, { title: webviewInput.getTitle(), contentOptions: webviewInput.webview.contentOptions, options: webviewInput.webview.options, diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 464f9a8cb19..d9bd443af2c 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -21,7 +21,7 @@ import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uri import { DEFAULT_EDITOR_ASSOCIATION, EditorExtensions, GroupIdentifier, IEditorFactoryRegistry, IResourceDiffEditorInput } from '../../../common/editor.js'; import { DiffEditorInput } from '../../../common/editor/diffEditorInput.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; -import { CONTEXT_ACTIVE_CUSTOM_EDITOR_ID, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, ICustomEditorService } from '../common/customEditor.js'; +import { CONTEXT_ACTIVE_CUSTOM_EDITOR_ID, CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CustomEditorCapabilities, CustomEditorInfo, CustomEditorInfoCollection, ICustomEditorModelManager, ICustomEditorService } from '../common/customEditor.js'; import { CustomEditorModelManager } from '../common/customEditorModelManager.js'; import { IEditorGroup, IEditorGroupContextKeyProvider, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { IEditorResolverService, IEditorType, RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js'; @@ -37,7 +37,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ private readonly _editorResolverDisposables = this._register(new DisposableStore()); private readonly _editorCapabilities = new Map(); - private readonly _models = new CustomEditorModelManager(); + private readonly _models: ICustomEditorModelManager; private readonly _onDidChangeEditorTypes = this._register(new Emitter()); public readonly onDidChangeEditorTypes: Event = this._onDidChangeEditorTypes.event; @@ -55,6 +55,8 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ) { super(); + this._models = new CustomEditorModelManager(this.uriIdentityService); + this._contributedEditors = this._register(new ContributedCustomEditors(storageService)); // Register the contribution points only emitting one change from the resolver this.editorResolverService.bufferChangeEvents(this.registerContributionPoints.bind(this)); diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts index 66d5b2b5989..7b5219d5e07 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModelManager.ts @@ -6,9 +6,17 @@ import { createSingleCallFunction } from '../../../../base/common/functional.js'; import { IReference } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; +import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { ICustomEditorModel, ICustomEditorModelManager } from './customEditor.js'; export class CustomEditorModelManager implements ICustomEditorModelManager { + private readonly _uriIdentityService: IUriIdentityService; + + constructor( + uriIdentityService: IUriIdentityService, + ) { + this._uriIdentityService = uriIdentityService; + } private readonly _references = new Map Date: Tue, 26 Nov 2024 19:53:31 +0900 Subject: [PATCH 002/479] open image --- .../markdown-language-features/package.json | 15 ++++++++++++- .../package.nls.json | 1 + .../preview-src/index.ts | 6 ++++- .../src/commands/index.ts | 2 ++ .../src/commands/openImage.ts | 22 +++++++++++++++++++ .../src/preview/preview.ts | 13 +++++++++++ .../types/previewMessaging.d.ts | 7 ++++++ 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 extensions/markdown-language-features/src/commands/openImage.ts diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 3f1c3a5fd58..a06a7af2f92 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -125,6 +125,11 @@ "title": "%markdown.copyImage.title%", "category": "Markdown" }, + { + "command": "_markdown.openImage", + "title": "%markdown.openImage.title%", + "category": "Markdown" + }, { "command": "markdown.showPreview", "title": "%markdown.preview.title%", @@ -189,7 +194,11 @@ "webview/context": [ { "command": "_markdown.copyImage", - "when": "webviewId == 'markdown.preview' && webviewSection == 'image'" + "when": "webviewId == 'markdown.preview' && (webviewSection == 'image' || webviewSection == 'localImage')" + }, + { + "command": "_markdown.openImage", + "when": "webviewId == 'markdown.preview' && webviewSection == 'localImage'" } ], "editor/title": [ @@ -244,6 +253,10 @@ } ], "commandPalette": [ + { + "command": "_markdown.openImage", + "when": "false" + }, { "command": "_markdown.copyImage", "when": "false" diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index da567b46665..47f26b05581 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -2,6 +2,7 @@ "displayName": "Markdown Language Features", "description": "Provides rich language support for Markdown.", "markdown.copyImage.title": "Copy Image", + "markdown.openImage.title": "Open Image", "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the Markdown preview. Setting it to `true` creates a `
` for newlines inside paragraphs.", "markdown.preview.linkify": "Convert URL-like text to links in the Markdown preview.", "markdown.preview.typographer": "Enable some language-neutral replacement and quotes beautification in the Markdown preview.", diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 33c84e0a384..cd2221b140d 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -132,7 +132,11 @@ function addImageContexts() { for (const img of images) { img.id = 'image-' + idNumber; idNumber += 1; - img.setAttribute('data-vscode-context', JSON.stringify({ webviewSection: 'image', id: img.id, 'preventDefaultContextMenuItems': true, resource: documentResource })); + const imageSource = img.src; + const imgSrcAttribute = img.getAttribute('src'); + const isLocalFile = imageSource !== imgSrcAttribute; + const webviewSection = isLocalFile ? 'localImage' : 'image'; + img.setAttribute('data-vscode-context', JSON.stringify({ webviewSection, id: img.id, 'preventDefaultContextMenuItems': true, resource: documentResource, imageSource })); } } diff --git a/extensions/markdown-language-features/src/commands/index.ts b/extensions/markdown-language-features/src/commands/index.ts index e1a4f2b41ff..382b2a8b6d5 100644 --- a/extensions/markdown-language-features/src/commands/index.ts +++ b/extensions/markdown-language-features/src/commands/index.ts @@ -18,6 +18,7 @@ import { CopyImageCommand } from './copyImage'; import { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector'; import { ShowSourceCommand } from './showSource'; import { ToggleLockCommand } from './toggleLock'; +import { OpenImageCommand } from './openImage'; export function registerMarkdownCommands( commandManager: CommandManager, @@ -28,6 +29,7 @@ export function registerMarkdownCommands( ): vscode.Disposable { const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager); + commandManager.register(new OpenImageCommand(previewManager)); commandManager.register(new CopyImageCommand(previewManager)); commandManager.register(new ShowPreviewCommand(previewManager, telemetryReporter)); commandManager.register(new ShowPreviewToSideCommand(previewManager, telemetryReporter)); diff --git a/extensions/markdown-language-features/src/commands/openImage.ts b/extensions/markdown-language-features/src/commands/openImage.ts new file mode 100644 index 00000000000..30026f7467d --- /dev/null +++ b/extensions/markdown-language-features/src/commands/openImage.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Command } from '../commandManager'; +import { MarkdownPreviewManager } from '../preview/previewManager'; + +export class OpenImageCommand implements Command { + public readonly id = '_markdown.openImage'; + + public constructor( + private readonly _webviewManager: MarkdownPreviewManager, + ) { } + + public execute(args: { resource: string, imageSource: string }) { + const source = vscode.Uri.parse(args.resource); + const { fsPath } = vscode.Uri.parse(args.imageSource); + this._webviewManager.findPreview(source)?.openImage(fsPath); + } +} diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index 7ccbc625b47..9e4ae20e064 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -443,6 +443,7 @@ export interface IManagedMarkdownPreview { readonly onDidChangeViewState: vscode.Event; copyImage(id: string): void; + openImage(imagePath: string): void; dispose(): void; refresh(): void; updateConfiguration(): void; @@ -524,6 +525,12 @@ export class StaticMarkdownPreview extends Disposable implements IManagedMarkdow }); } + openImage(imagePath: string): void { + const uri = vscode.Uri.file(imagePath); + this._webviewPanel.reveal(); + vscode.commands.executeCommand('vscode.open', uri); + } + private readonly _onDispose = this._register(new vscode.EventEmitter()); public readonly onDispose = this._onDispose.event; @@ -679,6 +686,12 @@ export class DynamicMarkdownPreview extends Disposable implements IManagedMarkdo }); } + openImage(imagePath: string): void { + const uri = vscode.Uri.file(imagePath); + this._webviewPanel.reveal(); + vscode.commands.executeCommand('vscode.open', uri); + } + private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter()); public readonly onDispose = this._onDisposeEmitter.event; diff --git a/extensions/markdown-language-features/types/previewMessaging.d.ts b/extensions/markdown-language-features/types/previewMessaging.d.ts index 05d10af6597..686b21bf1a9 100644 --- a/extensions/markdown-language-features/types/previewMessaging.d.ts +++ b/extensions/markdown-language-features/types/previewMessaging.d.ts @@ -71,10 +71,17 @@ export namespace ToWebviewMessage { readonly id: string; } + export interface OpenImageContent extends BaseMessage { + readonly type: 'openImage'; + readonly source: string; + readonly imageSource: string; + } + export type Type = | OnDidChangeTextEditorSelection | UpdateView | UpdateContent | CopyImageContent + | OpenImageContent ; } From 0715a50f6370fb99110104efa067b73938d1a3f1 Mon Sep 17 00:00:00 2001 From: notoriousmango Date: Wed, 27 Nov 2024 17:03:33 +0900 Subject: [PATCH 003/479] move openImage logic to command --- .../src/commands/openImage.ts | 6 +++--- .../src/preview/preview.ts | 13 ------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/extensions/markdown-language-features/src/commands/openImage.ts b/extensions/markdown-language-features/src/commands/openImage.ts index 30026f7467d..7a0562db446 100644 --- a/extensions/markdown-language-features/src/commands/openImage.ts +++ b/extensions/markdown-language-features/src/commands/openImage.ts @@ -14,9 +14,9 @@ export class OpenImageCommand implements Command { private readonly _webviewManager: MarkdownPreviewManager, ) { } - public execute(args: { resource: string, imageSource: string }) { + public execute(args: { resource: string; imageSource: string }) { const source = vscode.Uri.parse(args.resource); - const { fsPath } = vscode.Uri.parse(args.imageSource); - this._webviewManager.findPreview(source)?.openImage(fsPath); + const imageSource = vscode.Uri.file(vscode.Uri.parse(args.imageSource).path); + vscode.commands.executeCommand('vscode.open', imageSource, this._webviewManager.findPreview(source)); } } diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index 9e4ae20e064..7ccbc625b47 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -443,7 +443,6 @@ export interface IManagedMarkdownPreview { readonly onDidChangeViewState: vscode.Event; copyImage(id: string): void; - openImage(imagePath: string): void; dispose(): void; refresh(): void; updateConfiguration(): void; @@ -525,12 +524,6 @@ export class StaticMarkdownPreview extends Disposable implements IManagedMarkdow }); } - openImage(imagePath: string): void { - const uri = vscode.Uri.file(imagePath); - this._webviewPanel.reveal(); - vscode.commands.executeCommand('vscode.open', uri); - } - private readonly _onDispose = this._register(new vscode.EventEmitter()); public readonly onDispose = this._onDispose.event; @@ -686,12 +679,6 @@ export class DynamicMarkdownPreview extends Disposable implements IManagedMarkdo }); } - openImage(imagePath: string): void { - const uri = vscode.Uri.file(imagePath); - this._webviewPanel.reveal(); - vscode.commands.executeCommand('vscode.open', uri); - } - private readonly _onDisposeEmitter = this._register(new vscode.EventEmitter()); public readonly onDispose = this._onDisposeEmitter.event; From ea6463e38c6050c5b8dcf3bd9543483435cd8fd4 Mon Sep 17 00:00:00 2001 From: notoriousmango Date: Wed, 27 Nov 2024 17:25:41 +0900 Subject: [PATCH 004/479] rename --- .../markdown-language-features/src/commands/openImage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/commands/openImage.ts b/extensions/markdown-language-features/src/commands/openImage.ts index 7a0562db446..53f615148f3 100644 --- a/extensions/markdown-language-features/src/commands/openImage.ts +++ b/extensions/markdown-language-features/src/commands/openImage.ts @@ -16,7 +16,7 @@ export class OpenImageCommand implements Command { public execute(args: { resource: string; imageSource: string }) { const source = vscode.Uri.parse(args.resource); - const imageSource = vscode.Uri.file(vscode.Uri.parse(args.imageSource).path); - vscode.commands.executeCommand('vscode.open', imageSource, this._webviewManager.findPreview(source)); + const imageSourceUri = vscode.Uri.file(vscode.Uri.parse(args.imageSource).path); + vscode.commands.executeCommand('vscode.open', imageSourceUri, this._webviewManager.findPreview(source)); } } From 4685d75964105e78f64ad84a2abf898c7c91e8d8 Mon Sep 17 00:00:00 2001 From: Derek Yang Date: Wed, 4 Dec 2024 23:48:25 -0500 Subject: [PATCH 005/479] Fix CSS errors when using HTML escaped quotes --- .../server/src/modes/embeddedSupport.ts | 11 +++++++++++ .../server/src/test/embedded.test.ts | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/extensions/html-language-features/server/src/modes/embeddedSupport.ts b/extensions/html-language-features/server/src/modes/embeddedSupport.ts index 26ef68439da..a6874e043b4 100644 --- a/extensions/html-language-features/server/src/modes/embeddedSupport.ts +++ b/extensions/html-language-features/server/src/modes/embeddedSupport.ts @@ -198,6 +198,17 @@ function updateContent(c: EmbeddedRegion, content: string): string { if (!c.attributeValue && c.languageId === 'javascript') { return content.replace(``, ` */`); } + if (c.languageId === 'css') { + const quoteEscape = /("|")/g; + return content.replace(quoteEscape, (match, _, offset) => { + const spaces = ' '.repeat(match.length - 1); + const afterChar = content[offset + match.length]; + if (!afterChar || afterChar.includes(' ')) { + return `${spaces}"`; + } + return `"${spaces}`; + }); + } return content; } diff --git a/extensions/html-language-features/server/src/test/embedded.test.ts b/extensions/html-language-features/server/src/test/embedded.test.ts index 87698f39718..c5b7bca17ea 100644 --- a/extensions/html-language-features/server/src/test/embedded.test.ts +++ b/extensions/html-language-features/server/src/test/embedded.test.ts @@ -128,4 +128,11 @@ suite('HTML Embedded Support', () => { assertEmbeddedLanguageContent('
', 'javascript', ' return;\n foo(); '); }); + test('Script content - HTML escape characters', function (): any { + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial " } '); + }); + }); From 77f0483dcd560b2f905e1bf192eef0865a731562 Mon Sep 17 00:00:00 2001 From: Derek Yang Date: Thu, 5 Dec 2024 14:22:54 -0500 Subject: [PATCH 006/479] Add additional test case containing no escaped quotes --- .../html-language-features/server/src/test/embedded.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/html-language-features/server/src/test/embedded.test.ts b/extensions/html-language-features/server/src/test/embedded.test.ts index c5b7bca17ea..abba5b5858e 100644 --- a/extensions/html-language-features/server/src/test/embedded.test.ts +++ b/extensions/html-language-features/server/src/test/embedded.test.ts @@ -133,6 +133,7 @@ suite('HTML Embedded Support', () => { assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial " } '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: Arial} '); }); }); From c43ed48f5ffd68476d3c1ee0849a20032de4a558 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Fri, 6 Dec 2024 09:14:52 +0100 Subject: [PATCH 007/479] Bump version number to 1.97 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4c9991af6b..a16b3ecf06f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "code-oss-dev", - "version": "1.96.0", + "version": "1.97.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-oss-dev", - "version": "1.96.0", + "version": "1.97.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f7a7f68cde7..f283587b8c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.96.0", + "version": "1.97.0", "distro": "c883c91dadf5f063b26c86ffe01851acee3747c6", "author": { "name": "Microsoft Corporation" From 5addad41b0d7cb991e43a51341637fd8e19049b7 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:25:12 +0100 Subject: [PATCH 008/479] Git/SCM - fix GC warnings (#235464) --- extensions/git/src/repository.ts | 2 +- src/vs/workbench/contrib/scm/browser/menus.ts | 27 ++++++++++--------- .../contrib/scm/browser/scmHistoryViewPane.ts | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 1c2e6ca45c2..ef7997c7542 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -986,7 +986,7 @@ export class Repository implements Disposable { const actionButton = new ActionButton(this, this.commitCommandCenter, this.logger); this.disposables.push(actionButton); - actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button); + actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button, this, this.disposables); this._sourceControl.actionButton = actionButton.button; const progressManager = new ProgressManager(this); diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index 486f978b768..f14d8ea79f6 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -20,18 +20,6 @@ function actionEquals(a: IAction, b: IAction): boolean { return a.id === b.id; } -const repositoryMenuDisposables = new DisposableStore(); - -MenuRegistry.onDidChangeMenu(e => { - if (e.has(MenuId.SCMTitle)) { - repositoryMenuDisposables.clear(); - - for (const menuItem of MenuRegistry.getMenuItems(MenuId.SCMTitle)) { - repositoryMenuDisposables.add(MenuRegistry.appendMenuItem(MenuId.SCMSourceControlInline, menuItem)); - } - } -}); - export class SCMTitleMenu implements IDisposable { private _actions: IAction[] = []; @@ -244,6 +232,7 @@ export class SCMMenus implements ISCMMenus, IDisposable { readonly titleMenu: SCMTitleMenu; private readonly disposables = new DisposableStore(); + private readonly repositoryMenuDisposables = new DisposableStore(); private readonly menus = new Map void }>(); constructor( @@ -252,6 +241,20 @@ export class SCMMenus implements ISCMMenus, IDisposable { ) { this.titleMenu = instantiationService.createInstance(SCMTitleMenu); scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + + // Duplicate the `SCMTitle` menu items to the `SCMSourceControlInline` menu. We do this + // so that menu items can be independently hidden/shown in the "Source Control" and the + // "Source Control Repositories" views. + MenuRegistry.onDidChangeMenu(e => { + if (!e.has(MenuId.SCMTitle)) { + return; + } + + this.repositoryMenuDisposables.clear(); + for (const menuItem of MenuRegistry.getMenuItems(MenuId.SCMTitle)) { + this.repositoryMenuDisposables.add(MenuRegistry.appendMenuItem(MenuId.SCMSourceControlInline, menuItem)); + } + }, this, this.disposables); } private onDidRemoveRepository(repository: ISCMRepository): void { diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index d6057c14ecb..af17ed451bd 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -1372,7 +1372,7 @@ export class SCMHistoryViewPane extends ViewPane { } isFirstRun = false; })); - }); + }, this, this._store); } protected override layoutBody(height: number, width: number): void { From 266cc016110147732915cfce2387e4d7aaa51175 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 6 Dec 2024 10:33:32 +0100 Subject: [PATCH 009/479] Rerender hover on modifier key press - updates hover on hover definition preview request (#235406) rerender hover on modifier key press - updates hover on definition hover preview --- .../contrib/hover/browser/contentHoverController.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index ca0677d3206..9b8252eec14 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -244,10 +244,16 @@ export class ContentHoverController extends Disposable implements IEditorContrib return; } const isPotentialKeyboardShortcut = this._isPotentialKeyboardShortcut(e); - const isModifierKeyPressed = this._isModifierKeyPressed(e); - if (isPotentialKeyboardShortcut || isModifierKeyPressed) { + if (isPotentialKeyboardShortcut) { return; } + const isModifierKeyPressed = this._isModifierKeyPressed(e); + if (isModifierKeyPressed && this._mouseMoveEvent) { + const contentWidget: ContentHoverWidgetWrapper = this._getOrCreateContentWidget(); + if (contentWidget.showsOrWillShow(this._mouseMoveEvent)) { + return; + } + } this.hideContentHover(); } From c83840631aa2b50312b9d4207c6fa6519d62ade2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 6 Dec 2024 10:34:30 +0100 Subject: [PATCH 010/479] update codicons (#235469) --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 82204 -> 82588 bytes src/vs/base/common/codiconsLibrary.ts | 1 + 2 files changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 95c8a7a04c1c7b285ddd1e6ed88b0263c7333725..01f7648ad16f1fe1a31451e092708832703edd89 100644 GIT binary patch delta 7512 zcmXZh31C#!-39P-lFUr@1d>ch2q7dP30r_9B#^LgA_%ezVV4lLu!YDjgNT$;T!Yq9 ziWIG)B1N$-lp^j+aYLk5E!Eaq`rgzcrE0Aoe&_NR_`Nf4mN$3qeed3PFMGEJ_TCb> zqbPDMz+C|5&R;ff<**A^Jq08T2HFo^*tEW}rq7rgfwJzv<&PIFnAb4vp8b>b-cO`@ zk)DXDkJUpQm*PdsTGkb>{r+nmFA9VvH?5dI@8Gu2&*-HsK%uH z!SZ>_7PNky`|KF7G+s}BvvS4imfhV3ZU(#P^_;zT9xqCHAJk)MTRHusJEOh%errD0 zC%lc*db%s#KDwU|^61dmlwhB#D{naI)#0#%5Mi_yE=B3;kKcssmZ-mBj0 zeu6(PTGzTQpXUX8`qoWOB-mIJw$-FO2txe?d$4KCy=uEag4W;K3-_Zf#F+=CmD z&6lwsw{kR#n9KJ3HQvUfc%BdON4SIcatm+gt-OhM@Ta_sKfz1f&fSPX90Dp=5|WXE zRHPxjwSQt#(p=2L^_Y)FENq>a*e^02^_YQ~Sc+v>fo811I;_VAY{VvPMg&`M6Sm@J z+=A`cfgfWRZo{3}jk|F#euh2xIricg*oXV@OWcnK@GCrshj0*&;4vJ=ul1fIcpOLZ zJDk7^cm;3Z_jn8M;9b@1d-!7nAL1i?j6dNV{;c-?55B>F@g2S=NX9V85R;k0RAw-X zVP-Rj9hlEfEM#Yvu$&dFWEK0chJ9JfI`-!P4&^Wo=LnAERUE~uIfi3-Eyr^rCvgg= z@;XlAbk5~GMy}_4Hn5RP*~DdB!DhB_9oKUMH}OVp<;~p2TeyQi=1%V7ZM>7Myqo)Y z9}n_jKFY^ruT2)eQ-OHqTq7{j0QcYK<^;Zyhx>p6=Zv6AobG~eYfd4Tux0scxS^nOIz z;5v520ltpq_yhimzu;3$!~~2-7Cyj}cnY)7#5s5vOAy9uc%3~^fKH4>CQmRA|Hfzd z8$6uHKM=(~@h^Odi}*V}=Pz)EgK#^72q7K`xBwqtAP;XciSejIE^;t#5uU~~T!67$ z#I-ofwtR$#Fp8^jh_9eGyR#kJ@ET0TSDef>_z|`-!sY4-j^Vd_jjv(^XLAOo;Up88 zfR}YmXW&Kb#7InGKTN`8JfRCa*t#m^a_dP3T34h76Owfy>W?n+&8gWD^(5Z)h7*+Y zjV@An4F=CD7Z?syHX80#E;Q_>Tx58IaH}DYqN-g7AKTJM?&m;l0Y8hVDyUZ3u5y?lN?Rp5>kpH)4410792{ z8vayym!Zp6qplX-ZiAPUcN@Bbx#kh>Zv7(txdC&v=ZY-qe&HQ7>WJYzY}6&gd&H=7 zhUb2|VBt}(%?zrahIh)S$Ao~u1ke-7^*qn;h!yGDIHywgU#J-qjf z`h9pBLLH3&@Z9+#8Vlf^btkDd8W!OF!DxVh_kqz+0q>7Sg9f}0jZQ6i9~o>>erz

cz-h*t>Arb zG;YEByU_>+@4V4i2Jat6BNjZ*Xncd`JNhLW>fl9<20eKHG#dWkePJ{Z!uyxekO=RB z(clR0OQT^D-bJGU6W+g#hE8~wj0RD7|1la)bp?H;jc8Is6!-Asv3K(clh0 z&S;p2A21s5;RlU|e)u7yn*jLn#@%T72}U;t@RJO5Uiryte{G| zry1Q!z)v^2ses?c==K7BTZ2!O8Ai7p@H35WKHz5=-G;yq8+@ns_tgtJQ1$rjj4A|v zj-jiaTtino?G4>8&ogwtyn~^Bke_epepE+8_oE67T_HP3L>s!26&kvd6&bpM6&t$3 zbT)KfRAT79sMOFMu*}dMuZyAkTvtQ)xo(E#y8iv{+7VVLdl*(K^`gTnWlzIC%3g*w z%HD>3mG1MxTBZBEuukbdFYK>$pBD~LR$mbThw6blm~fcV9ZWb}+0Sr<(j8bhQt1vX zyh`Z~EF7hD2Nqtf9B4R3ImmFVa1wY@?a#;bz*V2{uyVQK5v8k2;p57chEFJ086H)- zDi%JeTy6M_(p9(cnDPe0-zwJ_9#=-zY9oA3>DokiQt6sR_`K3JiSR|`M#Gnsu6cyN zSGwjAo>Fc$d{e0(=*Pfzj4*cy# z6CU_KGMf6p-(fTfg74Z{G#!G!(`aG@|0f2nMs{JC+W@ZZZZ(=V!FTO1nn1z7-DoNW z{|=)`6?}JI22 z70gyXY&0i?|A@hGr8|K|^F;WEjAo4RA2WDWdDt+Z{Iy|$@`%yQvIzd;+7YdVz;`FM zu&45e$U9ywdYQ0;qR2s8a}N&X80SWJH18IQ~1XX-1F`;!g}Qi!&ypKZ=z`| z{F4SNmF}z;zN6GnkGKs_Yv)D7ca<+0x<+`}&^5vl-tb+)MYC}D?s5~& z$KkunP4H>!o{n{q54888(fl0#M@BPs_#Ye1+2Q}mphCu zrQ|S488nPmmcb~cS~lV~T3taj+rYi4ozaR5qB#b=mAQu9mFE z4WhbK9ekxMFr2J$JlaV+qQx9U3yqd_5G^v;rYts$C_5W1`XE|j+(K}))Mz0H(J}+| zDbX&5uPM74EfXQy&1q1HM(Bb2MZ($29!9H3h?X0zCn4I?;H0vbVWQI2jA*q9(F%i? zm6e8FT2qU&r@W}WYNORCL~D%Jr4a3FFh$wVXsrs-TBB7fMC**!ukfW#KL<~=t}K2u zl9%;NcuDxS@Mqbn*}2)fvOj4zy4@o=$vH!F8gn-1+?KO1=aHNfIj3?i2yoq^DdAswD}`!TC;^o%(lb>2#`aT;Z0&J(0rGMZJoei_R8T6fY@0-nqDQ zQ|Bi-Uo4qcvZ3T)$+42NrS+xjN_UqYDZO0QziegMzAouqws!fp>&UJvyI$xP?zXpk zukMF>RQ1^0KW5>aL>Q=n%Qeh@7&&dd!MOjThUmtq2gr4XO-!d<15!x zo~eqfYOFd?^-iB~pEZ3V5B7Pfx^4By>Lt}5)RfmOso7I=zVH6Nr~CEox4++|+UnYd z+D)~)YoDn7u=a9YLEXT*nROfL?y5Ud_gdZOb(i~x`;YIxtN*zH>jpeC;NyV<2TmQ> zJScup!Jxr|ng^X7bZPL&!OesB5B_{e@{sOBh7XxNWap5lhI~9UdFb|``-grttbEwI z$gl&$&J7O_pE`W!@DE2MkEk8dGUBcgXGdHfxozZ$tNLEGbyVZ1eWTvFy87z+s}Ej% ze)QE6k0C*L;t%#`6%HcfeQYVy=b?bIbxzrAkqbw{Qp zPOF$Ub=rn$r>9+<9-h8r`YZLQA6(y9zo-7pjQkm!W*nYzYG(Y*-7_!GS~u(5?18hN zoE@E0GH1)2OLJ@IJ~*#@-lgl?Uf*ke{`~Rt8|Uwuf3_j7p?|~jh9?#bUC^*#--2_E zF^%Po;~Tdx%v(5q;iiQzEZVT>fyKp<#d8_uQXp;J#ux+>Q`DCTK2UZZaLBNR?E2?2Hvpw zh7)Vz*Nj_pV$J!r)oYKho3?Jpx)0YUuOGF3-}+BBbl-5zh7%iOHV)soZ{vxL-)$<} zv~|<|8yjvsvpKY8^B0kd$nv9kWe=A{f|`$iuVvy4{U1lJZd6uiF=`i3Q&KgM75U6e zWl>>iNlj+BqIv+UN(zHn*>3ChYf4H>N~^2N8470g&ddsDhqHsBlAbII1&a!MGIZsc zaJD|~{tjSG^_9OReX3kmR_OiVNOm|YGZgMf_m)&rj|Rw#mrL zjSCeQb!e9rB;(=}lLE0!3nh2z(Jm_^H93^V*g(wI2m|pI8ELthZDSK+VgrFdTns}= zq0Vhe3p1~LYg$5|KqwUG>^5VA?mH7V$1x@*zAQ745K0XM!->JzxKL~$7|ci~Cntmw znUs){n4X#x6H^qIn2;2k8VrT<(n5jUP$)SNO3RB622+DIsiA~GX4gpDa5%6bHV_-5 zZ;#EW0ZRPDhl6&@@s4P{m4Q#-1{ zo-yUBO0`sVO=ZmwH66NAv8q>nzOrH<_0d9Ab#}O>syb2;?!e4oVM(9t!V*{8nZec= z#7PRPGwrJv{(iTvr%#7?0+vw0G+2PV2nW+nd z@$tc~ZZp&!K3#VTM=K|8f1mF^eXNuRRi9^{zP=k@h)vI$zhdR$rWGwc*3N5QzIge< Lr}KOAa-07LFYD&z delta 7080 zcmXBZ349dQ9S88=FUN*J4swu9LI@#&B*qv*2m!(k0mC8peTFMQxFJ9|WDyY+EdoKL zc$6wt1dAxPAhk#pDOyCdXj`>Nt+jM!OBE5RQtj{gm(O>8JDc6vH}B27H}Cn_J<*SC zjNX

Id*a05vlgPG3?xt^e~tYXu8#-PXh#9_M@yzLyPGvRdPfLKv0}H1&EQ$DVZ5lu`YnEkH}EFj!rOQU z=kcy;^Uopt1s~!ge2l;1B0j-2e1jYK4&UPkf@CxUY{odoGnuJu!8E2bi>;W$Hf+mw zyp0`L#*Qp!1v|4VE7^_R*@u1Ek5%l?0lb~n9K<0U%3&PAksQr29LsSW&uOe-XgX(b zCTFvb^SOYFxRlGeiVa-NwOq%$xRG~r6Yu4Hyq^#7C)~yz+{wrJ1fSu49^iBQC7EN{Wjc?Jvd0Y1gw zbeoOD2nk)k%hBt${2J*CNeN|27ZmBoP|3$hb!?ullUwTqMFNa zkZ+&^Z)K3pIT&N`HAis;Hsfxt=R*7jFXK3W&(rA7DV&ILc$Kkif>SI+3SPtg7=Y32 zf>C(k$fEdohIH$>|9AoLYK-nhc+(B8D`yxESI#uLQ{l}rIHsI!*h5)s_^5J@VUcpK zVZCym8Os#Cs583Z;mtP~rd(k7rgEX7JNP0)^<>^+qb>rT+ZR0CI6N^pu#@(d8+9D; z+|LT!sje{UOyI3F=%ZX^)WN`0U+G|78g)DH))}a`_U<$ss9bNDt5oee z%usGH%v9cG)MdfjXw-SZyW6N6gSW}xr1Bmh+swKA?o4by>Hal!~4KhyEf|f;r+>I0D$*r zqoDxaUyKF?cpn-K5AZ%R8Ytj>Y&2xR`>WC50q>&GFaqxrqX7lpC8MDw1n+O!5sfnN zJ~bL|;Qifb*>n zvyWc50|@&nyBPLUx`PR;l3&=|Qt5tNI9lm`TsTJQeq13&=|PU(JJI9@r>aGKH;L|CI7Z1fze z7p_#o8Ol2hXDVF@g|n5z4eOMyfWrAoS3uzc^CnQl%@baJkYIR=7&3!iL<2 z4cc*~6|PoJFkGvgXt+)}$+%gSKiP1ja*E;I%BhB%lO&NAGlbTuH{p{zCBshnf@xN@%H6Uupp&nR7u3HK{qjR_AZU5zbJ`|~-y zSZMf5rK>*S^UB4Bhm}hVUr@Sg6~3rkW_U#Ds#thbS#S8Va)se>I5CWt=wREPU)IZLVQmzTpNle zD)3z!il!{^T^oufFYxa%n#RE2Y&4;P@7h*0wSn*2Ry4_hf4|Xm2mThLi4T0&%mPncP)q}P4KrHOl@43mDeFbd+wW}i4^=@MpG*I zj~KYR*==~N@qAWZ$Xzp!8GNO5*O4$!>8>N8yXb#r=)S$zXkrHc=LW9zo-}mV-cyF| z`1=eCl)o@^t@X6K`?O)HcAhbs-@)H+Fh%*S(VP#yyJ!Vf%7aGpK=|(Z70n3YyX#kQ zT6xGYO8LBDYvp0X-e~-?Ra$(Bc8?h5H>PH{5AD(3F~e7s?gAG6T6x^?C8fK7MUzeV z?sEe7{%;K@C|%76Co5kynv}vnX|P!7PEPoaQrCFMZTPNsUN=0iJY~37`G%pZes`uq zSN*>?bk%>xaG&x`Ls$LoW)aPS;kye$kfd}si(o9K!GBvjq8T#$a|Z6mTnR<9X85j9 zqWLp?S6I*C%Tmb(oqlE+b*Nm1B z;D2qjm;nE}(Xs;kZw&sSykWG|0N?df0$0`F8ZAG-|ITP30{%^-C5gtn+LR1bS;I71 zn?P7~;%Jou;Yg$P3WTE!bXkO>4Vx+hhB1vd+H_l}yCNKCa8wy@w77wAg25fiM8i4C z=0-~&2qzi5uS_;{@1+<%t5i!lJlME5H<+_oON`cl5N>bqs-7sq zrP>k3Dmxf05g}Y=a7x+HXxRwia-#(#ggY56B_Uj4w5Wt|XQSmMgu57wR(3U7azeP$ zQ4?zQ318|0bMOMtxS{RAP+W@>Ej~@_k~Sl4QQDMQc z)l1nC**Vz*vTL)qWS`2u)H0__I0d#>HNb~p1A@{95h<)6*JTo6?-vS3@mse-G8vkE`B zW#}z?ZaGy{Ra95>#jTr)QG8$Vd$*SzT@$Z*UQ_M*Ok9j{%xmFdZ*e>dpez{D6ZI8aiZc%=kA@a zb;Q&uqPp{LxuJx|$UDJC*@7=v$?0vCM(>|qr zYWjTG=UU%EeK+>oK}GndTVHuJSv zfmyp|9h-G^cG>K)vp3G(H~YidCAIr%&(BGlb8gO!xeaq)3eC%&H(=g|dDrX8>Q>er ztGhP8a{lJ|7Z*e<7_#8Ah20l-UvhP6_R_vf4=w#*S;Vr;W!1~}EIYG2bNR95SL-9{ zlk0Qq%j$R3zqcZJ#pD%Fu1sFpedU%_%~w6Q>f?rzhMI=Q8m_J`U%g@V-ZjZ<#;)1D z=IYwywZqr0Tl>Mtp(^-@INo)sf=DXA46yx-pK^jB`w&wBA6CTYmpig%rB@YFJZv#1_Lqq1%a4AUT#55i;nJ%H2qos+H3Kzqa&jN$!${O$fj|rsexv>x$$u^fo3r=5s`@r zjEs(No|hjV8QC-{(4={j(!{u;l!(ZYNv)HTTIXdaCujEy7L?=%gZU)|!NKY2>5PpC zF)}$SDkUj9B`y#f8B>rF-8?NdHZmeAHV|kMNREye9*Axdm6Q;f5C}v?H_>OJ1Hr_M z02AX9LH7&I&ZCZxWqS7@v}w_EA!H+w7ztH+_OX{+`>Ur=;X}QRfu+ n;Rn@tvDORXkbdOAs8#Rt!qMDYxIgmf* Date: Fri, 6 Dec 2024 11:25:23 +0100 Subject: [PATCH 011/479] remove API guidelines from copilot instructions (#235475) --- .github/copilot-instructions.md | 134 -------------------------------- 1 file changed, 134 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 6b833803b2e..6f20c66c400 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -55,137 +55,3 @@ for (let i = 0, n = str.length; i < 10; i++) { function f(x: number, y: string): void { } ``` - -# Extension API Guidelines - -## Overview - -The following guidelines only apply to files in the `src/vscode-dts` folder. - -This is a loose collection of guidelines that you should be following when proposing API. The process for adding API is described here: [Extension API Process](https://github.com/Microsoft/vscode/wiki/Extension-API-process). - -## Core Principles - -### Avoid Breakage - -We DO NOT want to break API. Therefore be careful and conservative when proposing new API. It needs to hold up in the long term. Expose only the minimum but still try to anticipate potential future requests. - -### Namespaces - -The API is structured into different namespaces, like `commands`, `window`, `workspace` etc. Namespaces contain functions, constants, and events. All types (classes, enum, interfaces) are defined in the global, `vscode`, namespace. - -### JavaScript Feel - -The API should have a JavaScript’ish feel. While that is harder to put in rules, it means we use namespaces, properties, functions, and globals instead of object-factories and services. Also take inspiration from popular existing JS API, for instance `window.createStatusBarItem` is like `document.createElement`, the members of `DiagnosticsCollection` are similar to ES6 maps etc. - -### Global Events - -Events aren’t defined on the types they occur on but in the best matching namespace. For instance, document changes aren't sent by a document but via the `workspace.onDidChangeTextDocument` event. The event will contain the document in question. This **global event** pattern makes it easier to manage event subscriptions because changes happen less frequently. - -### Private Events - -Private or instance events aren't accessible via globals but exist on objects, e.g., `FileSystemWatcher#onDidCreate`. *Don't* use private events unless the sender of the event is private. The rule of thumb is: 'Objects that can be accessed globally (editors, tasks, terminals, documents, etc)' should not have private events, objects that are private (only known by its creators, like tree views, web views) can send private events' - -### Event Naming - -Events follow the `on[Did|Will]VerbSubject` patterns, like `onDidChangeActiveEditor` or `onWillSaveTextDocument`. It doesn’t hurt to use explicit names. - -### Creating Objects - -Objects that live in the main thread but can be controlled/instantiated by extensions are declared as interfaces, e.g. `TextDocument` or `StatusBarItem`. When you allow creating such objects your API must follow the `createXYZ(args): XYZ` pattern. Because this is a constructor-replacement, the call must return synchronously. - -### Shy Objects - -Objects the API hands out to extensions should not contain more than what the API defines. Don’t expect everyone to read `vscode.d.ts` but also expect folks to use debugging-aided-intellisense, meaning whatever the debugger shows developers will program against. We don’t want to appear as making false promises. Prefix your private members with `_` as that is a common rule or, even better, use function-scopes to hide information. - -### Sync vs. Async - -Reading data, like an editor selection, a configuration value, etc. is synchronous. Setting a state that reflects on the main side is asynchronous. Despite updates being async your ‘extension host object’ should reflect the new state synchronously. This happens when setting an editor selection - -``` - editor.selection = newSelection - - | - | - V - - 1. On the API object set the value as given - 2. Make an async-call to the main side ala `trySetSelection` - 3. The async-call returns with the actual selection (it might have changed in the meantime) - 4. On the API object set the value again -``` - -We usually don’t expose the fact that setting state is asynchronous. We try to have API that feels sync -`editor.selection` is a getter/setter and not a method. - -### Data Driven - -Whenever possible, you should define a data model and define provider-interfaces. This puts VS Code into control as we can decide when to ask those providers, how to deal with multiple providers etc. The `ReferenceProvider` interface is a good sample for this. - -### Enrich Data Incrementally - -Sometimes it is expensive for a provider to compute parts of its data. For instance, creating a full `CompletionItem` (with all the documentation and symbols resolved) conflicts with being able to compute a large list of them quickly. In those cases, providers should return a lightweight version and offer a `resolve` method that allows extensions to enrich data. The `CodeLensProvider` and `CompletionItemProvider` interfaces are good samples for this. - -### Cancellation - -Calls into a provider should always include a `CancellationToken` as the last parameter. With that, the main thread can signal to the provider that its result won’t be needed anymore. When adding new parameters to provider-functions, it is OK to have the token not at the end anymore. - -### Objects vs. Interfaces - -Objects that should be returned by a provider are usually represented by a class that extensions can instantiate, e.g. `CodeLens`. We do that to provide convenience constructors and to be able to populate default values. - -Data that we accept in methods calls, i.e., parameter types, like in `registerRenameProvider` or `showQuickPick`, are declared as interfaces. That makes it easy to fulfill the API contract using class-instances or plain object literals. - -### Strict and Relaxed Data - -Data the API returns is strict, e.g. `activeTextEditor` is an editor or `undefined`, but not `null`. On the other side, providers can return relaxed data. We usually accept 4 types: The actual type, like `Hover`, a `Thenable` of that type, `undefined` or `null`. With that we want to make it easy to implement a provider, e.g., if you can compute results synchronous you don’t need to wrap things into a promise or if a certain condition isn’t met simple return, etc. - -### Validate Data - -Although providers can return ‘relaxed’ data, you need to verify it. The same is true for arguments etc. Throw validation errors when possible, drop data object when invalid. - -### Copy Data - -Don’t send the data that a provider returned over the wire. Often it contains more information than we need and often there are cyclic dependencies. Use the provider data to create objects that your protocol speaks. - -### Enums - -When API-work started only numeric-enums were supported, today TypeScript supports string-or-types and string-enums. Because fewer concepts are better, we stick to numeric-enums. - -### Strict Null - -We define the API with strictNull-checks in mind. That means we use the optional annotation `foo?: number` and `null` or `undefined` in type annotations. For instance, its `activeTextEditor: TextEditor | undefined`. Again, be strict for types we define and relaxed when accepting data. - -### Undefined is False - -The default value of an optional, boolean property is `false`. This is for consistency with JS where undefined never evaluates to `true`. - -### JSDoc - -We add JSDoc for all parts of the API. The doc is supported by markdown syntax. When document string-datatypes that end up in the UI, use the phrase ‘Human-readable string…’ - -## Optional Parameters (`?` vs `| undefined`) - -* For implementation, treat omitting a parameter with `?` the same as explicitly passing in `undefined` -* Use `| undefined` when you want to callers to always have to consider the parameter. -* Use `?` when you want to allow callers to omit the parameter. -* Never use `?` and `| undefined` on a parameter. Instead follow the two rules above to decide which version to use. -* If adding a new parameter to an existing function, use `?` as this allows the new signature to be backwards compatible with the old version. -* Do not add an overload to add an optional parameter to the end of the function. Instead use `?`. - -## Optional Properties (`?` vs `| undefined`) - -* Do not write code that treats the absence of a property differently than a property being present but set to `undefined` - * This can sometimes hit you on spreads or iterating through objects, so just something to be aware of - -* For readonly properties on interfaces that VS Code exposes to extensions (this include managed objects, as well as the objects passed to events): - * Use `| undefined` as this makes it clear the property exists but has the value `undefined`. - -* For readonly properties on options bag type objects passed from extensions to VS Code: - * Use `?` when it is ok to omit the property - * Use `| undefined` when you want the user to have to pass in the property but `undefined` signals that you will fall back to some default - * Try to avoid `?` + `| undefined` in most cases. Instead use `?`. Using both `?` + `| undefined` isn't wrong, but it's often more clear to treat omitting the property as falling back to the default rather than passing in `undefined` - -* For unmanaged, writable objects: - * If using `?`, always also add `| undefined` unless want to allow the property to be omitted during initialization, but never allow users to explicitly set it to `undefined` afterwards. I don't think we have many cases where this will be needed - * In these cases, you may want to try changing the api to avoid this potential confusion - * If adding a new property to an unmanaged object, use `?` as this ensures the type is backwards compatible with the old version From 2443bffc6ccab10fbe32b8486055a59cd9ed99de Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Dec 2024 11:50:18 +0100 Subject: [PATCH 012/479] remove cyclic dependency `notebookChatEditController -> notebookChatActionsOverlay -> chatEditorOverlay` (#235480) * fix https://github.com/microsoft/vscode/issues/235383 * remove cyclic dependency `notebookChatEditController -> notebookChatActionsOverlay -> chatEditorOverlay` fyi @DonJayamanne --- .../chatMarkdownContentPart.ts | 15 +-- .../contrib/chat/browser/chatEditorOverlay.ts | 109 +----------------- 2 files changed, 6 insertions(+), 118 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts index 36ba8b16096..a281df1c40c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts @@ -35,7 +35,6 @@ import { IChatMarkdownContent } from '../../common/chatService.js'; import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; import { CodeBlockModelCollection } from '../../common/codeBlockModelCollection.js'; import { IChatCodeBlockInfo, IChatListItemRendererOptions } from '../chat.js'; -import { AnimatedValue, ObservableAnimatedValue } from '../chatEditorOverlay.js'; import { IChatRendererDelegate } from '../chatListRenderer.js'; import { ChatMarkdownDecorationsRenderer } from '../chatMarkdownDecorationsRenderer.js'; import { ChatEditorOptions } from '../chatOptions.js'; @@ -351,23 +350,15 @@ class CollapsedCodeBlock extends Disposable { this.element.title = this.labelService.getUriLabel(uri, { relative: false }); // Show a percentage progress that is driven by the rewrite - const slickRatio = ObservableAnimatedValue.const(0); - let t = Date.now(); + this._progressStore.add(autorun(r => { const rewriteRatio = modifiedEntry?.rewriteRatio.read(r); - if (rewriteRatio) { - slickRatio.changeAnimation(prev => { - const result = new AnimatedValue(prev.getValue(), rewriteRatio, Date.now() - t); - t = Date.now(); - return result; - }, undefined); - } const labelDetail = this.element.querySelector('.label-detail'); const isComplete = !modifiedEntry?.isCurrentlyBeingModified.read(r); if (labelDetail && !isStreaming && !isComplete) { - const value = slickRatio.getValue(undefined); - labelDetail.textContent = value === 0 ? localize('chat.codeblock.applying', "Applying edits...") : localize('chat.codeblock.applyingPercentage', "Applying edits ({0}%)...", Math.round(value * 100)); + const value = rewriteRatio; + labelDetail.textContent = value === 0 || !value ? localize('chat.codeblock.applying', "Applying edits...") : localize('chat.codeblock.applyingPercentage', "Applying edits ({0}%)...", Math.round(value * 100)); } else if (labelDetail && !isStreaming && isComplete) { iconEl.classList.remove(...iconClasses); const fileKind = uri.path.endsWith('/') ? FileKind.FOLDER : FileKind.FILE; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts index 978b13b59a1..6a09f5ef275 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts @@ -5,7 +5,7 @@ import './media/chatEditorOverlay.css'; import { DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { autorun, IReader, ISettableObservable, ITransaction, observableFromEvent, observableSignal, observableValue, transaction } from '../../../../base/common/observable.js'; +import { autorun, observableFromEvent, observableValue, transaction } from '../../../../base/common/observable.js'; import { isEqual } from '../../../../base/common/resources.js'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from '../../../../editor/browser/editorBrowser.js'; import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; @@ -17,7 +17,7 @@ import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionView import { ACTIVE_GROUP, IEditorService } from '../../../services/editor/common/editorService.js'; import { Range } from '../../../../editor/common/core/range.js'; import { IActionRunner } from '../../../../base/common/actions.js'; -import { $, append, EventLike, getWindow, reset, scheduleAtNextAnimationFrame } from '../../../../base/browser/dom.js'; +import { $, append, EventLike, reset } from '../../../../base/browser/dom.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; @@ -25,7 +25,6 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { assertType } from '../../../../base/common/types.js'; import { localize } from '../../../../nls.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; -import { ctxNotebookHasEditorModification } from '../../notebook/browser/contrib/chatEdit/notebookChatEditController.js'; import { AcceptAction, RejectAction } from './chatEditorActions.js'; import { ChatEditorController } from './chatEditorController.js'; @@ -195,22 +194,11 @@ class ChatEditorOverlayWidget implements IOverlayWidget { this._domNode.classList.toggle('busy', busy); })); - const slickRatio = ObservableAnimatedValue.const(0); - let t = Date.now(); this._showStore.add(autorun(r => { const value = activeEntry.rewriteRatio.read(r); - - slickRatio.changeAnimation(prev => { - const result = new AnimatedValue(prev.getValue(), value, Date.now() - t); - t = Date.now(); - return result; - }, undefined); - - const value2 = slickRatio.getValue(r); - reset(this._progressNode, (value === 0 ? localize('generating', "Generating edits...") - : localize('applyingPercentage', "{0}% Applying edits...", Math.round(value2 * 100)))); + : localize('applyingPercentage', "{0}% Applying edits...", Math.round(value * 100)))); })); this._showStore.add(autorun(r => { @@ -269,101 +257,10 @@ MenuRegistry.appendMenuItem(MenuId.ChatEditingEditorContent, { title: localize('label', "Navigation Status"), precondition: ContextKeyExpr.false(), }, - when: ctxNotebookHasEditorModification.negate(), group: 'navigate', order: -1 }); - -export class ObservableAnimatedValue { - public static const(value: number): ObservableAnimatedValue { - return new ObservableAnimatedValue(AnimatedValue.const(value)); - } - - private readonly _value: ISettableObservable; - - constructor( - initialValue: AnimatedValue, - ) { - this._value = observableValue(this, initialValue); - } - - setAnimation(value: AnimatedValue, tx: ITransaction | undefined): void { - this._value.set(value, tx); - } - - changeAnimation(fn: (prev: AnimatedValue) => AnimatedValue, tx: ITransaction | undefined): void { - const value = fn(this._value.get()); - this._value.set(value, tx); - } - - getValue(reader: IReader | undefined): number { - const value = this._value.read(reader); - if (!value.isFinished()) { - Scheduler.instance.invalidateOnNextAnimationFrame(reader); - } - return value.getValue(); - } -} - -class Scheduler { - static instance = new Scheduler(); - - private readonly _signal = observableSignal(this); - - private _isScheduled = false; - - invalidateOnNextAnimationFrame(reader: IReader | undefined): void { - this._signal.read(reader); - if (!this._isScheduled) { - this._isScheduled = true; - scheduleAtNextAnimationFrame(getWindow(undefined), () => { - this._isScheduled = false; - this._signal.trigger(undefined); - }); - } - } -} - -export class AnimatedValue { - - static const(value: number): AnimatedValue { - return new AnimatedValue(value, value, 0); - } - - readonly startTimeMs = Date.now(); - - constructor( - readonly startValue: number, - readonly endValue: number, - readonly durationMs: number, - ) { - if (startValue === endValue) { - this.durationMs = 0; - } - } - - isFinished(): boolean { - return Date.now() >= this.startTimeMs + this.durationMs; - } - - getValue(): number { - const timePassed = Date.now() - this.startTimeMs; - if (timePassed >= this.durationMs) { - return this.endValue; - } - const value = easeOutExpo(timePassed, this.startValue, this.endValue - this.startValue, this.durationMs); - return value; - } -} - -function easeOutExpo(passedTime: number, start: number, length: number, totalDuration: number): number { - return passedTime === totalDuration - ? start + length - : length * (-Math.pow(2, -10 * passedTime / totalDuration) + 1) + start; -} - - export class ChatEditorOverlayController implements IEditorContribution { static readonly ID = 'editor.contrib.chatOverlayController'; From 359ec5270a9c9220c418bb2e50a5b35e28b2aae1 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 6 Dec 2024 11:51:50 +0100 Subject: [PATCH 013/479] Add new line comment when pressing Enter from within line comment for languages that use double slash for line comment (#235407) * add decrease and increase indent patterns for cpp * adding new line with line comment on next line from within languages that use double slash for line comment --- extensions/cpp/language-configuration.json | 122 +++++++++++++++--- extensions/csharp/language-configuration.json | 100 +++++++++++--- extensions/go/language-configuration.json | 109 +++++++++++++--- extensions/groovy/language-configuration.json | 91 +++++++++++-- extensions/java/language-configuration.json | 104 ++++++++++++--- extensions/json/language-configuration.json | 82 ++++++++++-- extensions/less/language-configuration.json | 107 ++++++++++++--- .../objective-c/language-configuration.json | 91 +++++++++++-- extensions/php/language-configuration.json | 115 ++++++++++++++--- extensions/rust/language-configuration.json | 85 ++++++++++-- extensions/swift/language-configuration.json | 104 ++++++++++++--- 11 files changed, 941 insertions(+), 169 deletions(-) diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index 0bf8df9dc01..cb1fb733b99 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -1,29 +1,94 @@ { "comments": { "lineComment": "//", - "blockComment": ["/*", "*/"] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - { "open": "[", "close": "]" }, - { "open": "{", "close": "}" }, - { "open": "(", "close": ")" }, - { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "/*", "close": "*/", "notIn": ["string", "comment"] }, - { "open": "/**", "close": " */", "notIn": ["string"] } + { + "open": "[", + "close": "]" + }, + { + "open": "{", + "close": "}" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "/*", + "close": "*/", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["<", ">"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "<", + ">" + ] ], "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)", "folding": { @@ -32,6 +97,14 @@ "end": "^\\s*#pragma\\s+endregion\\b" } }, + "indentationRules": { + "decreaseIndentPattern": { + "pattern": "^\\s*[\\}\\]\\)].*$" + }, + "increaseIndentPattern": { + "pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$" + }, + }, "onEnterRules": [ { // Decrease indentation after single line if/else if/else, for, or while @@ -41,6 +114,19 @@ "action": { "indent": "outdent" } - } + }, + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index d8698b46c09..60814ae02f4 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -1,32 +1,100 @@ { "comments": { "lineComment": "//", - "blockComment": ["/*", "*/"] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["<", ">"], - ["'", "'"], - ["\"", "\""] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "<", + ">" + ], + [ + "'", + "'" + ], + [ + "\"", + "\"" + ] ], "folding": { "markers": { "start": "^\\s*#region\\b", "end": "^\\s*#endregion\\b" } - } + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] } diff --git a/extensions/go/language-configuration.json b/extensions/go/language-configuration.json index a5e06a56bad..9238bf3529b 100644 --- a/extensions/go/language-configuration.json +++ b/extensions/go/language-configuration.json @@ -1,28 +1,86 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "`", "close": "`", "notIn": ["string"]}, - { "open": "\"", "close": "\"", "notIn": ["string"]}, - { "open": "'", "close": "'", "notIn": ["string", "comment"]} + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "`", + "close": "`", + "notIn": [ + "string" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "`", + "`" + ] ], "indentationRules": { "increaseIndentPattern": "^.*(\\bcase\\b.*:|\\bdefault\\b:|(\\b(func|if|else|switch|select|for|struct)\\b.*)?{[^}\"'`]*|\\([^)\"'`]*)$", @@ -33,5 +91,20 @@ "start": "^\\s*//\\s*#?region\\b", "end": "^\\s*//\\s*#?endregion\\b" } - } -} \ No newline at end of file + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] +} diff --git a/extensions/groovy/language-configuration.json b/extensions/groovy/language-configuration.json index a81a8864a51..39e5fd4092c 100644 --- a/extensions/groovy/language-configuration.json +++ b/extensions/groovy/language-configuration.json @@ -1,25 +1,88 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] + ], + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/java/language-configuration.json b/extensions/java/language-configuration.json index 610adc686b4..6ba09bbd15c 100644 --- a/extensions/java/language-configuration.json +++ b/extensions/java/language-configuration.json @@ -1,28 +1,85 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] }, - { "open": "/**", "close": " */", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["<", ">"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "<", + ">" + ] ], "folding": { "markers": { @@ -97,6 +154,19 @@ "action": { "indent": "indent" } - } + }, + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index f9ec3fec781..d47efe2587e 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -1,22 +1,84 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"] + [ + "{", + "}" + ], + [ + "[", + "]" + ] ], "autoClosingPairs": [ - { "open": "{", "close": "}", "notIn": ["string"] }, - { "open": "[", "close": "]", "notIn": ["string"] }, - { "open": "(", "close": ")", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, - { "open": "`", "close": "`", "notIn": ["string", "comment"] } + { + "open": "{", + "close": "}", + "notIn": [ + "string" + ] + }, + { + "open": "[", + "close": "]", + "notIn": [ + "string" + ] + }, + { + "open": "(", + "close": ")", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "`", + "close": "`", + "notIn": [ + "string", + "comment" + ] + } ], "indentationRules": { "increaseIndentPattern": "({+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"}]*)$)|(\\[+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"\\]]*)$)", "decreaseIndentPattern": "^\\s*[}\\]],?\\s*$" - } + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] } diff --git a/extensions/less/language-configuration.json b/extensions/less/language-configuration.json index 7325d052704..71e155ddfcc 100644 --- a/extensions/less/language-configuration.json +++ b/extensions/less/language-configuration.json @@ -1,26 +1,88 @@ { "comments": { - "blockComment": ["/*", "*/"], + "blockComment": [ + "/*", + "*/" + ], "lineComment": "//" }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - { "open": "{", "close": "}", "notIn": ["string", "comment"] }, - { "open": "[", "close": "]", "notIn": ["string", "comment"] }, - { "open": "(", "close": ")", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, - { "open": "'", "close": "'", "notIn": ["string", "comment"] } + { + "open": "{", + "close": "}", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "[", + "close": "]", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "(", + "close": ")", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] ], "folding": { "markers": { @@ -32,5 +94,20 @@ "increaseIndentPattern": "(^.*\\{[^}]*$)", "decreaseIndentPattern": "^\\s*\\}" }, - "wordPattern": "(#?-?\\d*\\.\\d\\w*%?)|(::?[\\w-]+(?=[^,{;]*[,{]))|(([@#.!])?[\\w-?]+%?|[@#!.])" + "wordPattern": "(#?-?\\d*\\.\\d\\w*%?)|(::?[\\w-]+(?=[^,{;]*[,{]))|(([@#.!])?[\\w-?]+%?|[@#!.])", + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] } diff --git a/extensions/objective-c/language-configuration.json b/extensions/objective-c/language-configuration.json index a81a8864a51..39e5fd4092c 100644 --- a/extensions/objective-c/language-configuration.json +++ b/extensions/objective-c/language-configuration.json @@ -1,25 +1,88 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] + ], + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index f44d7a25cb6..d696ffa2950 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -1,28 +1,96 @@ { "comments": { "lineComment": "//", // "#" - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - { "open": "{", "close": "}", "notIn": ["string"] }, - { "open": "[", "close": "]", "notIn": ["string"] }, - { "open": "(", "close": ")", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, - { "open": "/**", "close": " */", "notIn": ["string"] } + { + "open": "{", + "close": "}", + "notIn": [ + "string" + ] + }, + { + "open": "[", + "close": "]", + "notIn": [ + "string" + ] + }, + { + "open": "(", + "close": ")", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["'", "'"], - ["\"", "\""], - ["`", "`"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "'", + "'" + ], + [ + "\"", + "\"" + ], + [ + "`", + "`" + ] ], "indentationRules": { "increaseIndentPattern": "({(?!.*}).*|\\(|\\[|((else(\\s)?)?if|else|for(each)?|while|switch|case).*:)\\s*((/[/*].*|)?$|\\?>)", @@ -85,6 +153,19 @@ "action": { "indent": "outdent" } - } + }, + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/rust/language-configuration.json b/extensions/rust/language-configuration.json index ecb0007f6ea..490f4409c65 100644 --- a/extensions/rust/language-configuration.json +++ b/extensions/rust/language-configuration.json @@ -1,25 +1,67 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["<", ">"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "<", + ">" + ] ], "indentationRules": { "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$", @@ -30,5 +72,20 @@ "start": "^\\s*//\\s*#?region\\b", "end": "^\\s*//\\s*#?endregion\\b" } - } + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] } diff --git a/extensions/swift/language-configuration.json b/extensions/swift/language-configuration.json index 54095ef5212..e1ceb1f6bc6 100644 --- a/extensions/swift/language-configuration.json +++ b/extensions/swift/language-configuration.json @@ -1,27 +1,99 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] }, - { "open": "`", "close": "`", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + }, + { + "open": "`", + "close": "`", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "`", + "`" + ] + ], + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } From c2037f152b2bb3119ce1d87f52987776540dd57f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 6 Dec 2024 12:01:44 +0100 Subject: [PATCH 014/479] chat - some setup and quotas tweaks (#235477) --- .../parts/titlebar/media/titlebarpart.css | 4 -- .../chat/browser/actions/chatActions.ts | 59 +++++++++++-------- .../contrib/chat/browser/chatQuotasService.ts | 2 +- .../contrib/chat/browser/chatSetup.ts | 26 +++++--- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 6aa44168c7b..0264e5460d4 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -207,10 +207,6 @@ border-color: var(--vscode-commandCenter-inactiveBorder) !important; } -.monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .codicon-copilot { - font-size: 14px; -} - .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-center > .window-title > .command-center .action-item.command-center-center:HOVER { color: var(--vscode-commandCenter-activeForeground); background-color: var(--vscode-commandCenter-activeBackground); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index ce421fb0491..cca372c6103 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -20,7 +20,7 @@ import { ILocalizedString, localize, localize2 } from '../../../../../nls.js'; import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js'; import { DropdownWithPrimaryActionViewItem } from '../../../../../platform/actions/browser/dropdownWithPrimaryActionViewItem.js'; import { Action2, MenuId, MenuItemAction, MenuRegistry, registerAction2, SubmenuItemAction } from '../../../../../platform/actions/common/actions.js'; -import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IsLinuxContext, IsWindowsContext } from '../../../../../platform/contextkey/common/contextkeys.js'; import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; @@ -571,9 +571,12 @@ export class ChatCommandCenterRendering extends Disposable implements IWorkbench @IChatAgentService agentService: IChatAgentService, @IChatQuotasService chatQuotasService: IChatQuotasService, @IInstantiationService instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, ) { super(); + const contextKeySet = new Set([ChatContextKeys.Setup.signedOut.key]); + actionViewItemService.register(MenuId.CommandCenter, MenuId.ChatCommandCenter, (action, options) => { if (!(action instanceof SubmenuItemAction)) { return undefined; @@ -587,29 +590,39 @@ export class ChatCommandCenterRendering extends Disposable implements IWorkbench const chatExtensionInstalled = agentService.getAgents().some(agent => agent.isDefault); const { chatQuotaExceeded, completionsQuotaExceeded } = chatQuotasService.quotas; - - let primaryAction: MenuItemAction; - if (chatExtensionInstalled && !chatQuotaExceeded && !completionsQuotaExceeded) { - primaryAction = instantiationService.createInstance(MenuItemAction, { - id: CHAT_OPEN_ACTION_ID, - title: OpenChatGlobalAction.TITLE, - icon: Codicon.copilot, - }, undefined, undefined, undefined, undefined); - } else if (!chatExtensionInstalled) { - primaryAction = instantiationService.createInstance(MenuItemAction, { - id: 'workbench.action.chat.triggerSetup', - title: localize2('triggerChatSetup', "Use AI Features with Copilot for Free..."), - icon: Codicon.copilot, - }, undefined, undefined, undefined, undefined); + const signedOut = contextKeyService.getContextKeyValue(ChatContextKeys.Setup.signedOut.key) ?? false; + + let primaryActionId: string; + let primaryActionTitle: string; + let primaryActionIcon: ThemeIcon; + if (!chatExtensionInstalled) { + primaryActionId = 'workbench.action.chat.triggerSetup'; + primaryActionTitle = localize('triggerChatSetup', "Use AI Features with Copilot for Free..."); + primaryActionIcon = Codicon.copilot; } else { - primaryAction = instantiationService.createInstance(MenuItemAction, { - id: OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, - title: quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }), - icon: Codicon.copilotWarning, - }, undefined, undefined, undefined, undefined); + if (signedOut) { + primaryActionId = CHAT_OPEN_ACTION_ID; + primaryActionTitle = localize('signInToChatSetup', "Sign in to Use Copilot..."); + primaryActionIcon = Codicon.copilotWarning; + } else if (chatQuotaExceeded || completionsQuotaExceeded) { + primaryActionId = OPEN_CHAT_QUOTA_EXCEEDED_DIALOG; + primaryActionTitle = quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }); + primaryActionIcon = Codicon.copilotWarning; + } else { + primaryActionId = CHAT_OPEN_ACTION_ID; + primaryActionTitle = OpenChatGlobalAction.TITLE.value; + primaryActionIcon = Codicon.copilot; + } } - - return instantiationService.createInstance(DropdownWithPrimaryActionViewItem, primaryAction, dropdownAction, action.actions, '', { ...options, skipTelemetry: true }); - }, Event.any(agentService.onDidChangeAgents, chatQuotasService.onDidChangeQuotas)); + return instantiationService.createInstance(DropdownWithPrimaryActionViewItem, instantiationService.createInstance(MenuItemAction, { + id: primaryActionId, + title: primaryActionTitle, + icon: primaryActionIcon, + }, undefined, undefined, undefined, undefined), dropdownAction, action.actions, '', { ...options, skipTelemetry: true }); + }, Event.any( + agentService.onDidChangeAgents, + chatQuotasService.onDidChangeQuotas, + Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(contextKeySet)) + )); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index 6ed345aacdb..11cb99aa8eb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -173,7 +173,7 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService ], custom: { closeOnLinkClick: true, - icon: Codicon.copilotWarning, + icon: Codicon.copilotWarningLarge, markdownDetails: [ { markdown: new MarkdownString(message, true) }, { markdown: new MarkdownString(upgradeToPro, true) } diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 0e6fa398837..b4eca48c3bb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -180,7 +180,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr await that.context.update({ triggered: true }); - showCopilotView(viewsService); + showCopilotView(viewsService, layoutService); ensureSideBarChatViewSize(400, viewDescriptorService, layoutService); if (startSetup === true && !ASK_FOR_PUBLIC_CODE_MATCHES) { @@ -616,7 +616,8 @@ class ChatSetupController extends Disposable { @IProgressService private readonly progressService: IProgressService, @IChatAgentService private readonly chatAgentService: IChatAgentService, @IActivityService private readonly activityService: IActivityService, - @ICommandService private readonly commandService: ICommandService + @ICommandService private readonly commandService: ICommandService, + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { super(); @@ -692,7 +693,7 @@ class ChatSetupController extends Disposable { let session: AuthenticationSession | undefined; let entitlement: ChatEntitlement | undefined; try { - showCopilotView(this.viewsService); + showCopilotView(this.viewsService, this.layoutService); session = await this.authenticationService.createSession(defaultChat.providerId, defaultChat.providerScopes[0]); entitlement = await this.requests.forceResolveEntitlement(session); } catch (error) { @@ -714,7 +715,7 @@ class ChatSetupController extends Disposable { const wasInstalled = this.context.state.installed; let didSignUp = false; try { - showCopilotView(this.viewsService); + showCopilotView(this.viewsService, this.layoutService); if (entitlement !== ChatEntitlement.Limited && entitlement !== ChatEntitlement.Pro && entitlement !== ChatEntitlement.Unavailable) { didSignUp = await this.requests.signUpLimited(session, options); @@ -749,7 +750,7 @@ class ChatSetupController extends Disposable { const currentActiveElement = getActiveElement(); if (activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body) { - (await showCopilotView(this.viewsService))?.focusInput(); + (await showCopilotView(this.viewsService, this.layoutService))?.focusInput(); } } } @@ -953,7 +954,7 @@ class ChatSetupContext extends Disposable { super(); this.checkExtensionInstallation(); - this.updateContext(); + this.updateContextSync(); } private async checkExtensionInstallation(): Promise { @@ -1014,6 +1015,10 @@ class ChatSetupContext extends Disposable { private async updateContext(): Promise { await this.updateBarrier?.wait(); + this.updateContextSync(); + } + + private updateContextSync(): void { this.logService.trace(`[chat setup] updateContext(): ${JSON.stringify(this._state)}`); if (this._state.triggered && !this._state.installed) { @@ -1060,7 +1065,14 @@ function isCopilotEditsViewActive(viewsService: IViewsService): boolean { return viewsService.getFocusedView()?.id === EditsViewId; } -function showCopilotView(viewsService: IViewsService): Promise { +function showCopilotView(viewsService: IViewsService, layoutService: IWorkbenchLayoutService): Promise { + + // Ensure main window is in front + if (layoutService.activeContainer !== layoutService.mainContainer) { + layoutService.mainContainer.focus(); + } + + // Bring up the correct view if (isCopilotEditsViewActive(viewsService)) { return showEditsView(viewsService); } else { From 9d3edd480c56d906565ecde462716867aa9379f9 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:15:35 +0100 Subject: [PATCH 015/479] SCM - dirty diff refactoring + clean-up (#235486) * DirtyDiff - clean-up original model variables * Add more information to the result object * A bit more cleanup * Switch to a DisposableMap --- src/vs/editor/common/diff/rangeMapping.ts | 36 ++-- .../api/browser/mainThreadEditors.ts | 14 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 202 ++++++++---------- .../workbench/contrib/scm/common/quickDiff.ts | 13 +- 4 files changed, 133 insertions(+), 132 deletions(-) diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts index 09021d118b7..f6565bdc4e2 100644 --- a/src/vs/editor/common/diff/rangeMapping.ts +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -383,28 +383,22 @@ export function getLineRangeMapping(rangeMapping: RangeMapping, originalLines: A return new DetailedLineRangeMapping(originalLineRange, modifiedLineRange, [rangeMapping]); } -export function lineRangeMappingFromChanges(changes: IChange[]): LineRangeMapping[] { - const lineRangeMapping: LineRangeMapping[] = []; - - for (const change of changes) { - let originalRange: LineRange; - if (change.originalEndLineNumber === 0) { - // Insertion - originalRange = new LineRange(change.originalStartLineNumber + 1, change.originalStartLineNumber + 1); - } else { - originalRange = new LineRange(change.originalStartLineNumber, change.originalEndLineNumber + 1); - } - - let modifiedRange: LineRange; - if (change.modifiedEndLineNumber === 0) { - // Deletion - modifiedRange = new LineRange(change.modifiedStartLineNumber + 1, change.modifiedStartLineNumber + 1); - } else { - modifiedRange = new LineRange(change.modifiedStartLineNumber, change.modifiedEndLineNumber + 1); - } +export function lineRangeMappingFromChange(change: IChange): LineRangeMapping { + let originalRange: LineRange; + if (change.originalEndLineNumber === 0) { + // Insertion + originalRange = new LineRange(change.originalStartLineNumber + 1, change.originalStartLineNumber + 1); + } else { + originalRange = new LineRange(change.originalStartLineNumber, change.originalEndLineNumber + 1); + } - lineRangeMapping.push(new LineRangeMapping(originalRange, modifiedRange)); + let modifiedRange: LineRange; + if (change.modifiedEndLineNumber === 0) { + // Deletion + modifiedRange = new LineRange(change.modifiedStartLineNumber + 1, change.modifiedStartLineNumber + 1); + } else { + modifiedRange = new LineRange(change.modifiedStartLineNumber, change.modifiedEndLineNumber + 1); } - return lineRangeMapping; + return new LineRangeMapping(originalRange, modifiedRange); } diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 93af5bc077d..73d81e729cb 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -166,7 +166,12 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } return observableFromEvent(this, dirtyDiffModel.onDidChange, () => { - return dirtyDiffModel.getQuickDiffResults(); + return dirtyDiffModel.getQuickDiffResults() + .map(result => ({ + original: result.original, + modified: result.modified, + changes: result.changes2 + })); }); } @@ -180,7 +185,12 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { } return observableFromEvent(Event.any(dirtyDiffModel.onDidChange, diffEditor.onDidUpdateDiff), () => { - const dirtyDiffInformation = dirtyDiffModel.getQuickDiffResults(); + const dirtyDiffInformation = dirtyDiffModel.getQuickDiffResults() + .map(result => ({ + original: result.original, + modified: result.modified, + changes: result.changes2 + })); const diffChanges = diffEditor.getDiffComputationResult()?.changes2 ?? []; const diffInformation = [{ diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 098df9c8c1b..7ca28f94265 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -7,7 +7,7 @@ import * as nls from '../../../../nls.js'; import './media/dirtydiffDecorator.css'; import { ThrottledDelayer } from '../../../../base/common/async.js'; -import { IDisposable, dispose, toDisposable, Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { IDisposable, dispose, toDisposable, Disposable, DisposableStore, DisposableMap } from '../../../../base/common/lifecycle.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import * as ext from '../../../common/contributions.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -57,12 +57,13 @@ import { ResourceMap } from '../../../../base/common/map.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; -import { IQuickDiffService, QuickDiff, QuickDiffResult } from '../common/quickDiff.js'; +import { IQuickDiffService, QuickDiff, QuickDiffChange, QuickDiffResult } from '../common/quickDiff.js'; import { IQuickDiffSelectItem, SwitchQuickDiffBaseAction, SwitchQuickDiffViewItem } from './dirtyDiffSwitcher.js'; import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; -import { lineRangeMappingFromChanges } from '../../../../editor/common/diff/rangeMapping.js'; +import { LineRangeMapping, lineRangeMappingFromChange } from '../../../../editor/common/diff/rangeMapping.js'; import { DiffState } from '../../../../editor/browser/widget/diffEditor/diffEditorViewModel.js'; import { toLineChanges } from '../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; +import { Iterable } from '../../../../base/common/iterator.js'; class DiffActionRunner extends ActionRunner { @@ -204,8 +205,10 @@ class DirtyDiffWidget extends PeekViewWidget { this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme, this)); this._applyTheme(themeService.getColorTheme()); - if (this.model.original.length > 0) { - contextKeyService = contextKeyService.createOverlay([['originalResourceScheme', this.model.original[0].uri.scheme], ['originalResourceSchemes', this.model.original.map(original => original.uri.scheme)]]); + if (!Iterable.isEmpty(this.model.originalTextModels)) { + contextKeyService = contextKeyService.createOverlay([ + ['originalResourceScheme', Iterable.first(this.model.originalTextModels)?.uri.scheme], + ['originalResourceSchemes', Iterable.map(this.model.originalTextModels, textModel => textModel.uri.scheme)]]); } this.create(); @@ -234,15 +237,13 @@ class DirtyDiffWidget extends PeekViewWidget { const labeledChange = this.model.changes[index]; const change = labeledChange.change; this._index = index; - this.contextKeyService.createKey('originalResourceScheme', this.model.changes[index].uri.scheme); + this.contextKeyService.createKey('originalResourceScheme', this.model.changes[index].original.scheme); this.updateActions(); this._provider = labeledChange.label; this.change = change; - const originalModel = this.model.original; - - if (!originalModel) { + if (Iterable.isEmpty(this.model.originalTextModels)) { return; } @@ -252,7 +253,7 @@ class DirtyDiffWidget extends PeekViewWidget { // non-side-by-side diff still hasn't created the view zones onFirstDiffUpdate(() => setTimeout(() => this.revealChange(change), 0)); - const diffEditorModel = this.model.getDiffEditorModel(labeledChange.uri.toString()); + const diffEditorModel = this.model.getDiffEditorModel(labeledChange.original); if (!diffEditorModel) { return; } @@ -291,7 +292,7 @@ class DirtyDiffWidget extends PeekViewWidget { } private renderTitle(label: string): void { - const providerChanges = this.model.mapChanges.get(label)!; + const providerChanges = this.model.quickDiffChanges.get(label)!; const providerIndex = providerChanges.indexOf(this._index); let detail: string; @@ -336,16 +337,8 @@ class DirtyDiffWidget extends PeekViewWidget { } private shouldUseDropdown(): boolean { - let providersWithChangesCount = 0; - if (this.model.mapChanges.size > 1) { - const keys = Array.from(this.model.mapChanges.keys()); - for (let i = 0; (i < keys.length) && (providersWithChangesCount <= 1); i++) { - if (this.model.mapChanges.get(keys[i])!.length > 0) { - providersWithChangesCount++; - } - } - } - return providersWithChangesCount >= 2; + return this.model.getQuickDiffResults() + .filter(quickDiff => quickDiff.changes.length > 0).length > 1; } private updateActions(): void { @@ -787,7 +780,7 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu 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); } else { - const providerChanges: number[] = this.model.mapChanges.get(this.widget.provider) ?? this.model.mapChanges.values().next().value!; + const providerChanges: number[] = this.model.quickDiffChanges.get(this.widget.provider) ?? this.model.quickDiffChanges.values().next().value!; const mapIndex = providerChanges.findIndex(value => value === this.widget!.index); index = providerChanges[rot(mapIndex + 1, providerChanges.length)]; } @@ -807,7 +800,7 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu if (this.editor.hasModel() && (typeof lineNumber === 'number')) { index = this.model.findPreviousClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber, true, this.widget.provider); } else { - const providerChanges: number[] = this.model.mapChanges.get(this.widget.provider) ?? this.model.mapChanges.values().next().value!; + const providerChanges: number[] = this.model.quickDiffChanges.get(this.widget.provider) ?? this.model.quickDiffChanges.values().next().value!; const mapIndex = providerChanges.findIndex(value => value === this.widget!.index); index = providerChanges[rot(mapIndex - 1, providerChanges.length)]; } @@ -879,7 +872,7 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu return true; } - private onDidModelChange(splices: ISplice[]): void { + private onDidModelChange(splices: ISplice[]): void { if (!this.model || !this.widget || this.widget.hasFocus()) { return; } @@ -1210,29 +1203,34 @@ export async function getOriginalResource(quickDiffService: IQuickDiffService, u return quickDiffs.length > 0 ? quickDiffs[0].originalResource : null; } -type LabeledChange = { change: IChange; label: string; uri: URI }; - export class DirtyDiffModel extends Disposable { - private _quickDiffs: QuickDiff[] = []; - private _originalModels: Map = new Map(); // key is uri.toString() - private _originalTextModels: ITextModel[] = []; private _model: ITextFileEditorModel; - get original(): ITextModel[] { return this._originalTextModels; } - private diffDelayer = new ThrottledDelayer(200); - private _quickDiffsPromise?: Promise; - private repositoryDisposables = new Set(); - private readonly originalModelDisposables = this._register(new DisposableStore()); + private readonly _originalEditorModels = new ResourceMap(); + private readonly _originalEditorModelsDisposables = this._register(new DisposableStore()); + get originalTextModels(): Iterable { + return Iterable.map(this._originalEditorModels.values(), editorModel => editorModel.textEditorModel); + } + private _disposed = false; + private _quickDiffs: QuickDiff[] = []; + private _quickDiffsPromise?: Promise; + private _diffDelayer = new ThrottledDelayer(200); + + private readonly _onDidChange = new Emitter<{ changes: QuickDiffChange[]; diff: ISplice[] }>(); + readonly onDidChange: Event<{ changes: QuickDiffChange[]; diff: ISplice[] }> = this._onDidChange.event; - private readonly _onDidChange = new Emitter<{ changes: LabeledChange[]; diff: ISplice[] }>(); - readonly onDidChange: Event<{ changes: LabeledChange[]; diff: ISplice[] }> = this._onDidChange.event; + private _changes: QuickDiffChange[] = []; + get changes(): QuickDiffChange[] { return this._changes; } - private _changes: LabeledChange[] = []; - get changes(): LabeledChange[] { return this._changes; } - private _mapChanges: Map = new Map(); // key is the quick diff name, value is the index of the change in this._changes - get mapChanges(): Map { return this._mapChanges; } + /** + * Map of quick diff name to the index of the change in `this.changes` + */ + private _quickDiffChanges: Map = new Map(); + get quickDiffChanges(): Map { return this._quickDiffChanges; } + + private readonly _repositoryDisposables = new DisposableMap(); constructor( textFileModel: IResolvedTextFileEditorModel, @@ -1260,10 +1258,9 @@ export class DirtyDiffModel extends Disposable { } this._register(this._model.onDidChangeEncoding(() => { - this.diffDelayer.cancel(); + this._diffDelayer.cancel(); this._quickDiffs = []; - this._originalModels.clear(); - this._originalTextModels = []; + this._originalEditorModels.clear(); this._quickDiffsPromise = undefined; this.setChanges([], new Map()); this.triggerDiff(); @@ -1280,65 +1277,56 @@ export class DirtyDiffModel extends Disposable { public getQuickDiffResults(): QuickDiffResult[] { return this._quickDiffs.map(quickDiff => { - const changes = this._changes - .filter(change => change.label === quickDiff.label) - .map(change => change.change); + const changes = this.changes + .filter(change => change.label === quickDiff.label); - // Convert IChange[] to LineRangeMapping[] - const lineRangeMappings = lineRangeMappingFromChanges(changes); return { + label: quickDiff.label, original: quickDiff.originalResource, modified: this._model.resource, - changes: lineRangeMappings + changes: changes.map(change => change.change), + changes2: changes.map(change => change.change2) }; }); } - public getDiffEditorModel(originalUri: string): IDiffEditorModel | undefined { - if (!this._originalModels.has(originalUri)) { - return; - } - const original = this._originalModels.get(originalUri)!; - - return { - modified: this._model.textEditorModel!, - original: original.textEditorModel - }; + public getDiffEditorModel(originalUri: URI): IDiffEditorModel | undefined { + const editorModel = this._originalEditorModels.get(originalUri); + return editorModel ? + { + modified: this._model.textEditorModel!, + original: editorModel.textEditorModel + } : undefined; } private onDidAddRepository(repository: ISCMRepository): void { const disposables = new DisposableStore(); - this.repositoryDisposables.add(disposables); - disposables.add(toDisposable(() => this.repositoryDisposables.delete(disposables))); - disposables.add(repository.provider.onDidChangeResources(this.triggerDiff, this)); - const onDidRemoveThis = Event.filter(this.scmService.onDidRemoveRepository, r => r === repository); - disposables.add(onDidRemoveThis(() => dispose(disposables), null)); + const onDidRemoveRepository = Event.filter(this.scmService.onDidRemoveRepository, r => r === repository); + disposables.add(onDidRemoveRepository(() => this._repositoryDisposables.deleteAndDispose(repository))); + + this._repositoryDisposables.set(repository, disposables); this.triggerDiff(); } private triggerDiff(): void { - if (!this.diffDelayer) { + if (!this._diffDelayer) { return; } - this.diffDelayer + this._diffDelayer .trigger(async () => { - const result: { changes: LabeledChange[]; mapChanges: Map } | null = await this.diff(); + const result: { changes: QuickDiffChange[]; mapChanges: Map } | null = await this.diff(); - const originalModels = Array.from(this._originalModels.values()); - if (!result || this._disposed || this._model.isDisposed() || originalModels.some(originalModel => originalModel.isDisposed())) { + const editorModels = Array.from(this._originalEditorModels.values()); + if (!result || this._disposed || this._model.isDisposed() || editorModels.some(editorModel => editorModel.isDisposed())) { return; // disposed } - if (originalModels.every(originalModel => originalModel.textEditorModel.getValueLength() === 0)) { - result.changes = []; - } - - if (!result.changes) { + if (editorModels.every(editorModel => editorModel.textEditorModel.getValueLength() === 0)) { result.changes = []; } @@ -1347,14 +1335,14 @@ export class DirtyDiffModel extends Disposable { .catch(err => onUnexpectedError(err)); } - private setChanges(changes: LabeledChange[], mapChanges: Map): void { - const diff = sortedDiff(this._changes, changes, (a, b) => compareChanges(a.change, b.change)); + private setChanges(changes: QuickDiffChange[], mapChanges: Map): void { + const diff = sortedDiff(this.changes, changes, (a, b) => compareChanges(a.change, b.change)); this._changes = changes; - this._mapChanges = mapChanges; + this._quickDiffChanges = mapChanges; this._onDidChange.fire({ changes, diff }); } - private diff(): Promise<{ changes: LabeledChange[]; mapChanges: Map } | null> { + private diff(): Promise<{ changes: QuickDiffChange[]; mapChanges: Map } | null> { return this.progressService.withProgress({ location: ProgressLocation.Scm, delay: 250 }, async () => { const originalURIs = await this.getQuickDiffsPromise(); if (this._disposed || this._model.isDisposed() || (originalURIs.length === 0)) { @@ -1371,14 +1359,18 @@ export class DirtyDiffModel extends Disposable { ? this.configurationService.getValue('diffEditor.ignoreTrimWhitespace') : ignoreTrimWhitespaceSetting !== 'false'; - const allDiffs: LabeledChange[] = []; + const allDiffs: QuickDiffChange[] = []; for (const quickDiff of filteredToDiffable) { const dirtyDiff = await this._diff(quickDiff.originalResource, this._model.resource, ignoreTrimWhitespace); - if (dirtyDiff) { - for (const diff of dirtyDiff) { - if (diff) { - allDiffs.push({ change: diff, label: quickDiff.label, uri: quickDiff.originalResource }); - } + if (dirtyDiff.changes && dirtyDiff.changes2 && dirtyDiff.changes.length === dirtyDiff.changes2.length) { + for (let index = 0; index < dirtyDiff.changes.length; index++) { + allDiffs.push({ + label: quickDiff.label, + original: quickDiff.originalResource, + modified: this._model.resource, + change: dirtyDiff.changes[index], + change2: dirtyDiff.changes2[index] + }); } } } @@ -1395,22 +1387,19 @@ export class DirtyDiffModel extends Disposable { }); } - private async _diff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { + private async _diff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<{ changes: readonly IChange[] | null; changes2: readonly LineRangeMapping[] | null }> { if (this.algorithm === undefined) { - return this.editorWorkerService.computeDirtyDiff(original, modified, ignoreTrimWhitespace); + const changes = await this.editorWorkerService.computeDirtyDiff(original, modified, ignoreTrimWhitespace); + return { changes, changes2: changes?.map(change => lineRangeMappingFromChange(change)) ?? null }; } - const diffResult = await this.editorWorkerService.computeDiff(original, modified, { + const result = await this.editorWorkerService.computeDiff(original, modified, { computeMoves: false, ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER }, this.algorithm); - if (!diffResult) { - return null; - } - - return toLineChanges(DiffState.fromDiffResult(diffResult)); + return { changes: result ? toLineChanges(DiffState.fromDiffResult(result)) : null, changes2: result?.changes ?? null }; } private getQuickDiffsPromise(): Promise { @@ -1425,8 +1414,7 @@ export class DirtyDiffModel extends Disposable { if (quickDiffs.length === 0) { this._quickDiffs = []; - this._originalModels.clear(); - this._originalTextModels = []; + this._originalEditorModels.clear(); return []; } @@ -1434,10 +1422,10 @@ export class DirtyDiffModel extends Disposable { return quickDiffs; } - this.originalModelDisposables.clear(); - this._originalModels.clear(); - this._originalTextModels = []; this._quickDiffs = quickDiffs; + + this._originalEditorModels.clear(); + this._originalEditorModelsDisposables.clear(); return (await Promise.all(quickDiffs.map(async (quickDiff) => { try { const ref = await this.textModelResolverService.createModelReference(quickDiff.originalResource); @@ -1446,8 +1434,7 @@ export class DirtyDiffModel extends Disposable { return []; } - this._originalModels.set(quickDiff.originalResource.toString(), ref.object); - this._originalTextModels.push(ref.object.textEditorModel); + this._originalEditorModels.set(quickDiff.originalResource, ref.object); if (isTextFileEditorModel(ref.object)) { const encoding = this._model.getEncoding(); @@ -1457,8 +1444,8 @@ export class DirtyDiffModel extends Disposable { } } - this.originalModelDisposables.add(ref); - this.originalModelDisposables.add(ref.object.textEditorModel.onDidChangeContent(() => this.triggerDiff())); + this._originalEditorModelsDisposables.add(ref); + this._originalEditorModelsDisposables.add(ref.object.textEditorModel.onDidChangeContent(() => this.triggerDiff())); return quickDiff; } catch (error) { @@ -1553,15 +1540,14 @@ export class DirtyDiffModel extends Disposable { } override dispose(): void { - super.dispose(); - this._disposed = true; + this._quickDiffs = []; - this._originalModels.clear(); - this._originalTextModels = []; - this.diffDelayer.cancel(); - this.repositoryDisposables.forEach(d => dispose(d)); - this.repositoryDisposables.clear(); + this._diffDelayer.cancel(); + this._originalEditorModels.clear(); + this._repositoryDisposables.dispose(); + + super.dispose(); } } diff --git a/src/vs/workbench/contrib/scm/common/quickDiff.ts b/src/vs/workbench/contrib/scm/common/quickDiff.ts index 0720dc9f5ff..3770f071c72 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiff.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiff.ts @@ -9,6 +9,7 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import { LanguageSelector } from '../../../../editor/common/languageSelector.js'; import { Event } from '../../../../base/common/event.js'; import { LineRangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; +import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; export const IQuickDiffService = createDecorator('quickDiff'); @@ -28,10 +29,20 @@ export interface QuickDiff { visible: boolean; } +export interface QuickDiffChange { + readonly label: string; + readonly original: URI; + readonly modified: URI; + readonly change: IChange; + readonly change2: LineRangeMapping; +} + export interface QuickDiffResult { + readonly label: string; readonly original: URI; readonly modified: URI; - readonly changes: readonly LineRangeMapping[]; + readonly changes: IChange[]; + readonly changes2: LineRangeMapping[]; } export interface IQuickDiffService { From 7b991b48600f10508f35ca67e368c4584548eb3e Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:16:04 +0100 Subject: [PATCH 016/479] Fix explorer find rerender error (#235488) fix #234129 --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 5951803d0ae..9f15f33a26b 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -562,7 +562,9 @@ export class ExplorerFindProvider implements IAsyncFindProvider { const tree = this.treeProvider(); for (const directory of highlightedDirectories) { - tree.rerender(directory); + if (tree.hasNode(directory)) { + tree.rerender(directory); + } } } From 354482357af4ff091191b9fa5c607bd23c6430c0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 6 Dec 2024 14:20:30 +0100 Subject: [PATCH 017/479] Await that decorations are added with definition hover preview before showing hover (#235489) returning promise --- .../contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts index 6f6f8323e02..70c093ee9d8 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts @@ -196,7 +196,7 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri return; } - this.textModelResolverService.createModelReference(result.uri).then(ref => { + return this.textModelResolverService.createModelReference(result.uri).then(ref => { if (!ref.object || !ref.object.textEditorModel) { ref.dispose(); From ad42640a78864e43008b8d5b750d04e96dd05218 Mon Sep 17 00:00:00 2001 From: Pankaj Khandelwal <44100113+pankajk07@users.noreply.github.com> Date: Fri, 6 Dec 2024 19:30:53 +0530 Subject: [PATCH 018/479] fix: disabled PlzDedicatedWorker in Electron (#233175) * fix: disabled PlzDedicatedWorker in Electron * chore: cleanup --------- Co-authored-by: deepak1556 --- src/main.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index 2d9c977a3bd..1504375283f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -288,8 +288,9 @@ function configureCommandlineSwitchesSync(cliArgs: NativeParsedArgs) { // Following features are disabled from the runtime: // `CalculateNativeWinOcclusion` - Disable native window occlusion tracker (https://groups.google.com/a/chromium.org/g/embedder-dev/c/ZF3uHHyWLKw/m/VDN2hDXMAAAJ) + // `PlzDedicatedWorker` - Refs https://github.com/microsoft/vscode/issues/233060#issuecomment-2523212427 const featuresToDisable = - `CalculateNativeWinOcclusion,${app.commandLine.getSwitchValue('disable-features')}`; + `CalculateNativeWinOcclusion,PlzDedicatedWorker,${app.commandLine.getSwitchValue('disable-features')}`; app.commandLine.appendSwitch('disable-features', featuresToDisable); // Blink features to configure. From 5d8ea135391debc0ccabf6d6cb9c35e572ef469b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 6 Dec 2024 15:48:28 +0100 Subject: [PATCH 019/479] Ben/active-ocelot (#235494) * chat - make quota reset date explicitly `undefined` if neede * chat - tweak status quota indication * chat - tweak status indicator further --- .../contrib/chat/browser/chatQuotasService.ts | 50 +++++++++++++------ .../contrib/chat/browser/chatSetup.ts | 2 +- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index 11cb99aa8eb..efa9304c974 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -36,9 +36,9 @@ export interface IChatQuotasService { } export interface IChatQuotas { - readonly chatQuotaExceeded: boolean; - readonly completionsQuotaExceeded: boolean; - readonly quotaResetDate: Date; + chatQuotaExceeded: boolean; + completionsQuotaExceeded: boolean; + quotaResetDate: Date | undefined; } export const OPEN_CHAT_QUOTA_EXCEEDED_DIALOG = 'workbench.action.chat.openQuotaExceededDialog'; @@ -50,7 +50,7 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService private readonly _onDidChangeQuotas = this._register(new Emitter()); readonly onDidChangeQuotas: Event = this._onDidChangeQuotas.event; - private _quotas = { chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: new Date(0) }; + private _quotas: IChatQuotas = { chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: undefined }; get quotas(): IChatQuotas { return this._quotas; } private readonly chatQuotaExceededContextKey = ChatContextKeys.chatQuotaExceeded.bindTo(this.contextKeyService); @@ -227,7 +227,7 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService clearQuotas(): void { if (this.quotas.chatQuotaExceeded || this.quotas.completionsQuotaExceeded) { - this.acceptQuotas({ chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: new Date(0) }); + this.acceptQuotas({ chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: undefined }); } } @@ -241,6 +241,8 @@ export class ChatQuotasStatusBarEntry extends Disposable implements IWorkbenchCo static readonly ID = 'chat.quotasStatusBarEntry'; + private static readonly COPILOT_STATUS_ID = 'GitHub.copilot.status'; // TODO@bpasero unify into 1 core indicator + private readonly _entry = this._register(new MutableDisposable()); constructor( @@ -254,19 +256,39 @@ export class ChatQuotasStatusBarEntry extends Disposable implements IWorkbenchCo private updateStatusbarEntry(): void { const { chatQuotaExceeded, completionsQuotaExceeded } = this.chatQuotasService.quotas; + + // Some quota exceeded, show indicator if (chatQuotaExceeded || completionsQuotaExceeded) { - // Some quota exceeded, show indicator + let text: string; + if (chatQuotaExceeded && !completionsQuotaExceeded) { + text = localize('chatQuotaExceededStatus', "Chat limit reached"); + } else if (completionsQuotaExceeded && !chatQuotaExceeded) { + text = localize('completionsQuotaExceededStatus', "Completions limit reached"); + } else { + text = localize('chatAndCompletionsQuotaExceededStatus', "Copilot limit reached"); + } + + const isCopilotStatusVisible = this.statusbarService.isEntryVisible(ChatQuotasStatusBarEntry.COPILOT_STATUS_ID); + if (!isCopilotStatusVisible) { + text = `$(copilot-warning) ${text}`; + } + this._entry.value = this.statusbarService.addEntry({ - name: localize('indicator', "Copilot Quota Indicator"), - text: localize('limitReached', "Copilot Limit Reached"), - ariaLabel: localize('copilotQuotaExceeded', "Copilot Limit Reached"), + name: localize('indicator', "Copilot Limit Indicator"), + text, + ariaLabel: text, command: OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, - kind: 'prominent', showInAllWindows: true, - tooltip: quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }), - }, ChatQuotasStatusBarEntry.ID, StatusbarAlignment.RIGHT, { id: 'GitHub.copilot.status', alignment: StatusbarAlignment.RIGHT }); // TODO@bpasero unify into 1 core indicator - } else { - // No quota exceeded, remove indicator + tooltip: quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }) + }, ChatQuotasStatusBarEntry.ID, StatusbarAlignment.RIGHT, { + id: ChatQuotasStatusBarEntry.COPILOT_STATUS_ID, + alignment: StatusbarAlignment.RIGHT, + compact: isCopilotStatusVisible + }); + } + + // No quota exceeded, remove indicator + else { this._entry.clear(); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index b4eca48c3bb..27c17e06172 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -512,7 +512,7 @@ class ChatSetupRequests extends Disposable { this.chatQuotasService.acceptQuotas({ chatQuotaExceeded: typeof state.quotas.chat === 'number' ? state.quotas.chat <= 0 : false, completionsQuotaExceeded: typeof state.quotas.completions === 'number' ? state.quotas.completions <= 0 : false, - quotaResetDate: state.quotas.resetDate ? new Date(state.quotas.resetDate) : new Date(0) + quotaResetDate: state.quotas.resetDate ? new Date(state.quotas.resetDate) : undefined }); } } From 33ba5efde7fd9fbd635acf4c2f3d605b4b871576 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 6 Dec 2024 16:13:56 +0100 Subject: [PATCH 020/479] Use AI features for free button does nothing in new profile (fix microsoft/vscode-copilot#11066) (#235495) --- src/vs/workbench/contrib/chat/browser/chat.contribution.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 95062868665..2aa243649de 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -124,6 +124,7 @@ configurationRegistry.registerConfiguration({ 'chat.experimental.offerSetup': { type: 'boolean', default: false, + scope: ConfigurationScope.APPLICATION, markdownDescription: nls.localize('chat.experimental.offerSetup', "Controls whether setup is offered for Chat if not done already."), tags: ['experimental', 'onExP'] }, From 96098190240692109dd0de866cdbf74090377823 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Dec 2024 16:23:47 +0100 Subject: [PATCH 021/479] makes inline chat hint stable wrt blame decoration (#235497) - change from injected text to after decoration - use class name for awol attachedData https://github.com/microsoft/vscode-copilot/issues/10943 --- .../browser/inlineChatCurrentLine.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts index 5f48988ff06..8cf0f0891a6 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts @@ -16,7 +16,7 @@ import { Range } from '../../../../editor/common/core/range.js'; import { IPosition, Position } from '../../../../editor/common/core/position.js'; import { AbstractInlineChatAction } from './inlineChatActions.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { InjectedTextCursorStops, IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js'; import { URI } from '../../../../base/common/uri.js'; import { isEqual } from '../../../../base/common/resources.js'; import { StandardTokenType } from '../../../../editor/common/encodedTokenAttributes.js'; @@ -36,6 +36,8 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { Event } from '../../../../base/common/event.js'; import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; +import { createStyleSheet2 } from '../../../../base/browser/domStylesheets.js'; +import { stringValue } from '../../../../base/browser/cssValue.js'; export const CTX_INLINE_CHAT_SHOWING_HINT = new RawContextKey('inlineChatShowingHint', false, localize('inlineChatShowingHint', "Whether inline chat shows a contextual hint")); @@ -149,12 +151,6 @@ export class ShowInlineChatHintAction extends EditorAction2 { } } -class HintData { - constructor( - readonly setting: string - ) { } -} - export class InlineChatHintsController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.inlineChatHints'; @@ -193,8 +189,7 @@ export class InlineChatHintsController extends Disposable implements IEditorCont if (e.target.type !== MouseTargetType.CONTENT_TEXT) { return; } - const attachedData = e.target.detail.injectedText?.options.attachedData; - if (!(attachedData instanceof HintData)) { + if (!e.target.element?.classList.contains('inline-chat-hint-text')) { return; } if (e.event.leftButton) { @@ -202,7 +197,10 @@ export class InlineChatHintsController extends Disposable implements IEditorCont this.hide(); } else if (e.event.rightButton) { e.event.preventDefault(); - this._showContextMenu(e.event, attachedData.setting); + this._showContextMenu(e.event, e.target.element?.classList.contains('whitespace') + ? InlineChatConfigKeys.LineEmptyHint + : InlineChatConfigKeys.LineNLHint + ); } })); @@ -251,6 +249,9 @@ export class InlineChatHintsController extends Disposable implements IEditorCont return undefined; }); + const style = createStyleSheet2(); + this._store.add(style); + this._store.add(autorun(r => { const showData = showDataObs.read(r); @@ -264,7 +265,7 @@ export class InlineChatHintsController extends Disposable implements IEditorCont const agentName = chatAgentService.getDefaultAgent(ChatAgentLocation.Editor)?.name ?? localize('defaultTitle', "Chat"); const { position, isEol, isWhitespace, kb, model } = showData; - const inlineClassName: string[] = ['inline-chat-hint']; + const inlineClassName: string[] = ['a' /*HACK but sorts as we want*/, 'inline-chat-hint', 'inline-chat-hint-text']; let content: string; if (isWhitespace) { content = '\u00a0' + localize('title2', "{0} to edit with {1}", kb, agentName); @@ -275,6 +276,11 @@ export class InlineChatHintsController extends Disposable implements IEditorCont inlineClassName.push('embedded'); } + style.setStyle(`.inline-chat-hint-text::after { content: ${stringValue(content)} }`); + if (isWhitespace) { + inlineClassName.push('whitespace'); + } + this._ctxShowingHint.set(true); decos.set([{ @@ -283,13 +289,7 @@ export class InlineChatHintsController extends Disposable implements IEditorCont description: 'inline-chat-hint-line', showIfCollapsed: true, stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - after: { - content, - inlineClassName: inlineClassName.join(' '), - inlineClassNameAffectsLetterSpacing: true, - cursorStops: InjectedTextCursorStops.None, - attachedData: new HintData(isWhitespace ? InlineChatConfigKeys.LineEmptyHint : InlineChatConfigKeys.LineNLHint) - } + afterContentClassName: inlineClassName.join(' '), } }]); From f7069b2feada75d95faf3806e35e507cfa8b6fed Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:41:46 +0100 Subject: [PATCH 022/479] Use `disregardIgnoreFiles` in file search queries (#235501) Use `disregardIgnoreFiles` from file search --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 9f15f33a26b..4de8cc1c244 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -604,7 +604,10 @@ export class ExplorerFindProvider implements IAsyncFindProvider { const searchExcludePattern = getExcludes(this.configurationService.getValue({ resource: root.resource })) || {}; const searchOptions: IFileQuery = { - folderQueries: [{ folder: root.resource }], + folderQueries: [{ + folder: root.resource, + disregardIgnoreFiles: !this.configurationService.getValue('explorer.excludeGitIgnore'), + }], type: QueryType.File, shouldGlobMatchFilePattern: true, cacheKey: `explorerfindprovider:${root.name}:${rootIndex}:${this.sessionId}`, From 8270a86019db7551da42b71d15c6080a414d8c81 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 6 Dec 2024 16:55:37 +0100 Subject: [PATCH 023/479] Update grammars (#235506) --- extensions/dart/cgmanifest.json | 2 +- extensions/dart/syntaxes/dart.tmLanguage.json | 47 +++--- extensions/docker/cgmanifest.json | 4 +- .../docker/syntaxes/docker.tmLanguage.json | 19 ++- extensions/go/cgmanifest.json | 4 +- extensions/go/syntaxes/go.tmLanguage.json | 137 +++++++++++++++++- extensions/latex/cgmanifest.json | 2 +- extensions/perl/cgmanifest.json | 2 +- 8 files changed, 178 insertions(+), 39 deletions(-) diff --git a/extensions/dart/cgmanifest.json b/extensions/dart/cgmanifest.json index da493cafa70..5558b78af1d 100644 --- a/extensions/dart/cgmanifest.json +++ b/extensions/dart/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dart-lang/dart-syntax-highlight", "repositoryUrl": "https://github.com/dart-lang/dart-syntax-highlight", - "commitHash": "e8b053f9834cb44db0f49ac4a4567177bd943dbf" + "commitHash": "e1ac5c446c2531343393adbe8fff9d45d8a7c412" } }, "licenseDetail": [ diff --git a/extensions/dart/syntaxes/dart.tmLanguage.json b/extensions/dart/syntaxes/dart.tmLanguage.json index 32ea3f5b0c3..b4f80b680bd 100644 --- a/extensions/dart/syntaxes/dart.tmLanguage.json +++ b/extensions/dart/syntaxes/dart.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dart-lang/dart-syntax-highlight/commit/e8b053f9834cb44db0f49ac4a4567177bd943dbf", + "version": "https://github.com/dart-lang/dart-syntax-highlight/commit/e1ac5c446c2531343393adbe8fff9d45d8a7c412", "name": "Dart", "scopeName": "source.dart", "patterns": [ @@ -66,6 +66,16 @@ } ], "repository": { + "dartdoc-codeblock-triple": { + "begin": "^\\s*///\\s*(?!\\s*```)", + "end": "\n", + "contentName": "variable.other.source.dart" + }, + "dartdoc-codeblock-block": { + "begin": "^\\s*\\*\\s*(?!(\\s*```|/))", + "end": "\n", + "contentName": "variable.other.source.dart" + }, "dartdoc": { "patterns": [ { @@ -77,30 +87,31 @@ } }, { - "match": "^ {4,}(?![ \\*]).*", - "captures": { - "0": { - "name": "variable.name.source.dart" + "begin": "^\\s*///\\s*(```)", + "end": "^\\s*///\\s*(```)|^(?!\\s*///)", + "patterns": [ + { + "include": "#dartdoc-codeblock-triple" } - } + ] }, { - "contentName": "variable.other.source.dart", - "begin": "```.*?$", - "end": "```" + "begin": "^\\s*\\*\\s*(```)", + "end": "^\\s*\\*\\s*(```)|^(?=\\s*\\*/)", + "patterns": [ + { + "include": "#dartdoc-codeblock-block" + } + ] }, { - "match": "(`[^`]+?`)", - "captures": { - "0": { - "name": "variable.other.source.dart" - } - } + "match": "`[^`\n]+`", + "name": "variable.other.source.dart" }, { - "match": "(\\* (( ).*))$", + "match": "(?:\\*|\\/\\/)\\s{4,}(.*?)(?=($|\\*\\/))", "captures": { - "2": { + "1": { "name": "variable.other.source.dart" } } @@ -154,7 +165,7 @@ { "name": "comment.block.documentation.dart", "begin": "///", - "while": "^\\s*///", + "end": "^(?!\\s*///)", "patterns": [ { "include": "#dartdoc" diff --git a/extensions/docker/cgmanifest.json b/extensions/docker/cgmanifest.json index 4f568542aed..8462de7dd72 100644 --- a/extensions/docker/cgmanifest.json +++ b/extensions/docker/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "language-docker", "repositoryUrl": "https://github.com/moby/moby", - "commitHash": "abd39744c6f3ed854500e423f5fabf952165161f" + "commitHash": "c2029cb2574647e4bc28ed58486b8e85883eedb9" } }, "license": "Apache-2.0", @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/docker/syntaxes/docker.tmLanguage.json b/extensions/docker/syntaxes/docker.tmLanguage.json index f7f414636c4..aa5223a31ea 100644 --- a/extensions/docker/syntaxes/docker.tmLanguage.json +++ b/extensions/docker/syntaxes/docker.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/moby/moby/commit/abd39744c6f3ed854500e423f5fabf952165161f", + "version": "https://github.com/moby/moby/commit/c2029cb2574647e4bc28ed58486b8e85883eedb9", "name": "Dockerfile", "scopeName": "source.dockerfile", "patterns": [ @@ -41,6 +41,9 @@ }, "match": "^\\s*(?i:(ONBUILD)\\s+)?(?i:(CMD|ENTRYPOINT))\\s" }, + { + "include": "#string-character-escape" + }, { "begin": "\"", "beginCaptures": { @@ -57,8 +60,7 @@ "name": "string.quoted.double.dockerfile", "patterns": [ { - "match": "\\\\.", - "name": "constant.character.escaped.dockerfile" + "include": "#string-character-escape" } ] }, @@ -78,8 +80,7 @@ "name": "string.quoted.single.dockerfile", "patterns": [ { - "match": "\\\\.", - "name": "constant.character.escaped.dockerfile" + "include": "#string-character-escape" } ] }, @@ -98,5 +99,11 @@ "comment": "comment.line", "match": "^(\\s*)((#).*$\\n?)" } - ] + ], + "repository": { + "string-character-escape": { + "name": "constant.character.escaped.dockerfile", + "match": "\\\\." + } + } } \ No newline at end of file diff --git a/extensions/go/cgmanifest.json b/extensions/go/cgmanifest.json index 5276d2824a7..a6dbd5d1bf0 100644 --- a/extensions/go/cgmanifest.json +++ b/extensions/go/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "go-syntax", "repositoryUrl": "https://github.com/worlpaker/go-syntax", - "commitHash": "32bbaebcf218fa552e8f0397401e12f6e94fa3c5" + "commitHash": "fbdaec061157e98dda185c0ce771ce6a2c793045" } }, "license": "MIT", "description": "The file syntaxes/go.tmLanguage.json is from https://github.com/worlpaker/go-syntax, which in turn was derived from https://github.com/jeff-hykin/better-go-syntax.", - "version": "0.7.8" + "version": "0.7.9" } ], "version": 1 diff --git a/extensions/go/syntaxes/go.tmLanguage.json b/extensions/go/syntaxes/go.tmLanguage.json index ed6ead03480..db17cad3f91 100644 --- a/extensions/go/syntaxes/go.tmLanguage.json +++ b/extensions/go/syntaxes/go.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/worlpaker/go-syntax/commit/32bbaebcf218fa552e8f0397401e12f6e94fa3c5", + "version": "https://github.com/worlpaker/go-syntax/commit/fbdaec061157e98dda185c0ce771ce6a2c793045", "name": "Go", "scopeName": "source.go", "patterns": [ @@ -97,7 +97,10 @@ "comment": "all statements related to variables", "patterns": [ { - "include": "#var_const_assignment" + "include": "#const_assignment" + }, + { + "include": "#var_assignment" }, { "include": "#variable_assignment" @@ -2645,12 +2648,12 @@ } ] }, - "var_const_assignment": { - "comment": "variable assignment with var and const keyword", + "var_assignment": { + "comment": "variable assignment with var keyword", "patterns": [ { - "comment": "var and const with single type assignment", - "match": "(?:(?<=\\bvar\\b|\\bconst\\b)(?:\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", + "comment": "single assignment", + "match": "(?:(?<=\\bvar\\b)(?:\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", "captures": { "1": { "patterns": [ @@ -2696,8 +2699,8 @@ } }, { - "comment": "var and const with multi type assignment", - "begin": "(?:(?<=\\bvar\\b|\\bconst\\b)(?:\\s*)(\\())", + "comment": "multi assignment", + "begin": "(?:(?<=\\bvar\\b)(?:\\s*)(\\())", "beginCaptures": { "1": { "name": "punctuation.definition.begin.bracket.round.go" @@ -2763,6 +2766,124 @@ } ] }, + "const_assignment": { + "comment": "constant assignment with const keyword", + "patterns": [ + { + "comment": "single assignment", + "match": "(?:(?<=\\bconst\\b)(?:\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#delimiters" + }, + { + "match": "\\w+", + "name": "variable.other.constant.go" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#type-declarations-without-brackets" + }, + { + "include": "#generic_types" + }, + { + "match": "\\(", + "name": "punctuation.definition.begin.bracket.round.go" + }, + { + "match": "\\)", + "name": "punctuation.definition.end.bracket.round.go" + }, + { + "match": "\\[", + "name": "punctuation.definition.begin.bracket.square.go" + }, + { + "match": "\\]", + "name": "punctuation.definition.end.bracket.square.go" + }, + { + "match": "\\w+", + "name": "entity.name.type.go" + } + ] + } + } + }, + { + "comment": "multi assignment", + "begin": "(?:(?<=\\bconst\\b)(?:\\s*)(\\())", + "beginCaptures": { + "1": { + "name": "punctuation.definition.begin.bracket.round.go" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.end.bracket.round.go" + } + }, + "patterns": [ + { + "match": "(?:(?:^\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#delimiters" + }, + { + "match": "\\w+", + "name": "variable.other.constant.go" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#type-declarations-without-brackets" + }, + { + "include": "#generic_types" + }, + { + "match": "\\(", + "name": "punctuation.definition.begin.bracket.round.go" + }, + { + "match": "\\)", + "name": "punctuation.definition.end.bracket.round.go" + }, + { + "match": "\\[", + "name": "punctuation.definition.begin.bracket.square.go" + }, + { + "match": "\\]", + "name": "punctuation.definition.end.bracket.square.go" + }, + { + "match": "\\w+", + "name": "entity.name.type.go" + } + ] + } + } + }, + { + "include": "$self" + } + ] + } + ] + }, "variable_assignment": { "comment": "variable assignment", "patterns": [ diff --git a/extensions/latex/cgmanifest.json b/extensions/latex/cgmanifest.json index d937ba4f430..25b52bf3787 100644 --- a/extensions/latex/cgmanifest.json +++ b/extensions/latex/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "jlelong/vscode-latex-basics", "repositoryUrl": "https://github.com/jlelong/vscode-latex-basics", - "commitHash": "df6ef817c932d24da5cc72927344a547e463cc65" + "commitHash": "59971565a7065dbb617576c04add9d891b056319" } }, "license": "MIT", diff --git a/extensions/perl/cgmanifest.json b/extensions/perl/cgmanifest.json index cd175abe37d..b7c850dd1aa 100644 --- a/extensions/perl/cgmanifest.json +++ b/extensions/perl/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "textmate/perl.tmbundle", "repositoryUrl": "https://github.com/textmate/perl.tmbundle", - "commitHash": "a85927a902d6e5d7805f56a653f324d34dfad53a" + "commitHash": "d9841a0878239fa43f88c640f8d458590f97e8f5" } }, "licenseDetail": [ From d6faa5b076c07a4a7ef25f74e21501bc145a06a4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Dec 2024 17:30:17 +0100 Subject: [PATCH 024/479] debt - use `observableConfigValue` (#235508) --- .../inlineChat/browser/inlineChatCurrentLine.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts index 8cf0f0891a6..43e73454efd 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts @@ -33,11 +33,11 @@ import { IContextMenuService } from '../../../../platform/contextview/browser/co import { toAction } from '../../../../base/common/actions.js'; import { IMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { Event } from '../../../../base/common/event.js'; import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; import { createStyleSheet2 } from '../../../../base/browser/domStylesheets.js'; import { stringValue } from '../../../../base/browser/cssValue.js'; +import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; export const CTX_INLINE_CHAT_SHOWING_HINT = new RawContextKey('inlineChatShowingHint', false, localize('inlineChatShowingHint', "Whether inline chat shows a contextual hint")); @@ -208,10 +208,9 @@ export class InlineChatHintsController extends Disposable implements IEditorCont const decos = this._editor.createDecorationsCollection(); const editorObs = observableCodeEditor(editor); - const keyObs = observableFromEvent(keybindingService.onDidUpdateKeybindings, _ => keybindingService.lookupKeybinding(ACTION_START)?.getLabel()); - - const configSignal = observableFromEvent(Event.filter(_configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(InlineChatConfigKeys.LineEmptyHint) || e.affectsConfiguration(InlineChatConfigKeys.LineNLHint)), () => undefined); + const configHintEmpty = observableConfigValue(InlineChatConfigKeys.LineEmptyHint, false, this._configurationService); + const configHintNL = observableConfigValue(InlineChatConfigKeys.LineNLHint, false, this._configurationService); const showDataObs = derived(r => { const ghostState = ghostCtrl?.model.read(r)?.state.read(r); @@ -222,8 +221,6 @@ export class InlineChatHintsController extends Disposable implements IEditorCont const kb = keyObs.read(r); - configSignal.read(r); - if (ghostState !== undefined || !kb || !position || !model || !textFocus) { return undefined; } @@ -237,12 +234,12 @@ export class InlineChatHintsController extends Disposable implements IEditorCont const isWhitespace = model.getLineLastNonWhitespaceColumn(position.lineNumber) === 0 && model.getValueLength() > 0 && position.column > 1; if (isWhitespace) { - return _configurationService.getValue(InlineChatConfigKeys.LineEmptyHint) + return configHintEmpty.read(r) ? { isEol, isWhitespace, kb, position, model } : undefined; } - if (visible && isEol && _configurationService.getValue(InlineChatConfigKeys.LineNLHint)) { + if (visible && isEol && configHintNL.read(r)) { return { isEol, isWhitespace, kb, position, model }; } From 0f695e4d81b4f89cb8e3833062994fe30c7160f8 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 6 Dec 2024 17:37:47 +0100 Subject: [PATCH 025/479] chore - remove `IChatEditingService#onDidCreateEditingSession` in favor of `currentEditingSessionObs` --- .../chat/browser/actions/chatTitleActions.ts | 6 ++---- .../browser/chatEditing/chatEditingService.ts | 6 ------ .../contrib/chat/browser/chatWidget.ts | 7 ++++++- .../contrib/chatInputRelatedFilesContrib.ts | 18 +++++++++--------- .../contrib/chat/common/chatEditingService.ts | 1 - .../test/browser/inlineChatController.test.ts | 3 ++- 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index 7d395f051d8..10dd97aff86 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from '../../../../../base/common/codicons.js'; -import { Event } from '../../../../../base/common/event.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ResourceSet } from '../../../../../base/common/map.js'; import { marked } from '../../../../../base/common/marked/marked.js'; +import { waitForState } from '../../../../../base/common/observable.js'; import { basename } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; @@ -465,9 +465,7 @@ export function registerChatTitleActions() { let editingSession = chatEditingService.currentEditingSessionObs.get(); if (!editingSession) { - await Event.toPromise(chatEditingService.onDidCreateEditingSession); - editingSession = chatEditingService.currentEditingSessionObs.get(); - return; + editingSession = await waitForState(chatEditingService.currentEditingSessionObs); } if (!editingSession) { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts index f1204bd40ed..34b24c1dbb8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts @@ -61,11 +61,6 @@ export class ChatEditingService extends Disposable implements IChatEditingServic return this._currentSessionObs; } - private readonly _onDidCreateEditingSession = this._register(new Emitter()); - get onDidCreateEditingSession() { - return this._onDidCreateEditingSession.event; - } - private readonly _onDidChangeEditingSession = this._register(new Emitter()); public readonly onDidChangeEditingSession = this._onDidChangeEditingSession.event; @@ -220,7 +215,6 @@ export class ChatEditingService extends Disposable implements IChatEditingServic })); this._currentSessionObs.set(session, undefined); - this._onDidCreateEditingSession.fire(session); this._onDidChangeEditingSession.fire(); return session; } diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index a8831878ddb..800e82d952e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -15,6 +15,7 @@ import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable, combinedDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { ResourceSet } from '../../../../base/common/map.js'; import { Schemas } from '../../../../base/common/network.js'; +import { autorun } from '../../../../base/common/observable.js'; import { extUri, isEqual } from '../../../../base/common/resources.js'; import { isDefined } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; @@ -250,7 +251,11 @@ export class ChatWidget extends Disposable implements IChatWidget { const chatEditingSessionDisposables = this._register(new DisposableStore()); - this._register(this.chatEditingService.onDidCreateEditingSession((session) => { + this._register(autorun(r => { + const session = this.chatEditingService.currentEditingSessionObs.read(r); + if (!session) { + return; + } if (session.chatSessionId !== this.viewModel?.sessionId) { // this chat editing session is for a different chat widget return; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts index ff1938c5b7b..3f4045b9ce0 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts @@ -7,10 +7,11 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Event } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../../base/common/map.js'; +import { autorun } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; import { localize } from '../../../../../nls.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; -import { ChatEditingSessionChangeType, IChatEditingService, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { ChatEditingSessionChangeType, IChatEditingService, IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { IChatWidgetService } from '../chat.js'; export class ChatRelatedFilesContribution extends Disposable implements IWorkbenchContribution { @@ -25,10 +26,12 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben ) { super(); - this._handleNewEditingSession(); - this._register(this.chatEditingService.onDidCreateEditingSession(() => { + this._register(autorun(r => { this.chatEditingSessionDisposables.clear(); - this._handleNewEditingSession(); + const session = this.chatEditingService.currentEditingSessionObs.read(r); + if (session) { + this._handleNewEditingSession(session); + } })); } @@ -95,11 +98,8 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben } - private _handleNewEditingSession() { - const currentEditingSession = this.chatEditingService.currentEditingSessionObs.get(); - if (!currentEditingSession) { - return; - } + private _handleNewEditingSession(currentEditingSession: IChatEditingSession) { + const widget = this.chatWidgetService.getWidgetBySessionId(currentEditingSession.chatSessionId); if (!widget || widget.viewModel?.sessionId !== currentEditingSession.chatSessionId) { return; diff --git a/src/vs/workbench/contrib/chat/common/chatEditingService.ts b/src/vs/workbench/contrib/chat/common/chatEditingService.ts index e102d7525e9..c0598e39eba 100644 --- a/src/vs/workbench/contrib/chat/common/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/common/chatEditingService.ts @@ -23,7 +23,6 @@ export interface IChatEditingService { _serviceBrand: undefined; - readonly onDidCreateEditingSession: Event; /** * emitted when a session is created, changed or disposed */ diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index feaba4ca832..7d07547770e 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -70,6 +70,7 @@ import { IChatEditingService, IChatEditingSession } from '../../../chat/common/c import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; import { TextModelResolverService } from '../../../../services/textmodelResolver/common/textModelResolverService.js'; import { ChatInputBoxContentProvider } from '../../../chat/browser/chatEdinputInputContentProvider.js'; +import { IObservable, observableValue } from '../../../../../base/common/observable.js'; suite('InlineChatController', function () { @@ -162,7 +163,7 @@ suite('InlineChatController', function () { [IInlineChatSessionService, new SyncDescriptor(InlineChatSessionServiceImpl)], [ICommandService, new SyncDescriptor(TestCommandService)], [IChatEditingService, new class extends mock() { - override onDidCreateEditingSession: Event = Event.None; + override currentEditingSessionObs: IObservable = observableValue(this, null); }], [IInlineChatSavingService, new class extends mock() { override markChanged(session: Session): void { From d89fdcbfa8d0d9242b3fdb3528d792370380128a Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 6 Dec 2024 18:43:21 +0100 Subject: [PATCH 026/479] Removing bottom padding on editor hover (#235507) * almost there * fixing padding, but now does not render correctly * adding changes * removing line * renaming class * taking into account borders --- .../hover/browser/contentHoverWidget.ts | 29 +++++++++++-------- src/vs/editor/contrib/hover/browser/hover.css | 10 +++---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts index ad27c1544d5..ffb8aaa9f11 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts @@ -19,7 +19,6 @@ import { Emitter } from '../../../../base/common/event.js'; import { RenderedContentHover } from './contentHoverRendered.js'; const HORIZONTAL_SCROLLING_BY = 30; -const CONTAINER_HEIGHT_PADDING = 6; export class ContentHoverWidget extends ResizableContentWidget { @@ -68,6 +67,7 @@ export class ContentHoverWidget extends ResizableContentWidget { dom.append(this._resizableNode.domNode, this._hover.containerDomNode); this._resizableNode.domNode.style.zIndex = '50'; + this._resizableNode.domNode.className = 'monaco-resizable-hover'; this._register(this._editor.onDidLayoutChange(() => { if (this.isVisible) { @@ -117,9 +117,15 @@ export class ContentHoverWidget extends ResizableContentWidget { return ContentHoverWidget._applyDimensions(containerDomNode, width, height); } + private _setScrollableElementDimensions(width: number | string, height: number | string): void { + const scrollbarDomElement = this._hover.scrollbar.getDomNode(); + return ContentHoverWidget._applyDimensions(scrollbarDomElement, width, height); + } + private _setHoverWidgetDimensions(width: number | string, height: number | string): void { - this._setContentsDomNodeDimensions(width, height); this._setContainerDomNodeDimensions(width, height); + this._setScrollableElementDimensions(width, height); + this._setContentsDomNodeDimensions(width, height); this._layoutContentWidget(); } @@ -176,12 +182,11 @@ export class ContentHoverWidget extends ResizableContentWidget { if (!availableSpace) { return; } - // Padding needed in order to stop the resizing down to a smaller height - let maximumHeight = CONTAINER_HEIGHT_PADDING; + const children = this._hover.contentsDomNode.children; + let maximumHeight = children.length - 1; Array.from(this._hover.contentsDomNode.children).forEach((hoverPart) => { maximumHeight += hoverPart.clientHeight; }); - return Math.min(availableSpace, maximumHeight); } @@ -209,7 +214,7 @@ export class ContentHoverWidget extends ResizableContentWidget { const initialWidth = ( typeof this._contentWidth === 'undefined' ? 0 - : this._contentWidth - 2 // - 2 for the borders + : this._contentWidth ); if (overflowing || this._hover.containerDomNode.clientWidth < initialWidth) { @@ -217,7 +222,7 @@ export class ContentHoverWidget extends ResizableContentWidget { const horizontalPadding = 14; return bodyBoxWidth - horizontalPadding; } else { - return this._hover.containerDomNode.clientWidth + 2; + return this._hover.containerDomNode.clientWidth; } } @@ -389,16 +394,16 @@ export class ContentHoverWidget extends ResizableContentWidget { public onContentsChanged(): void { this._removeConstraintsRenderNormally(); - const containerDomNode = this._hover.containerDomNode; + const contentsDomNode = this._hover.contentsDomNode; - let height = dom.getTotalHeight(containerDomNode); - let width = dom.getTotalWidth(containerDomNode); + let height = dom.getTotalHeight(contentsDomNode); + let width = dom.getTotalWidth(contentsDomNode) + 2; this._resizableNode.layout(height, width); this._setHoverWidgetDimensions(width, height); - height = dom.getTotalHeight(containerDomNode); - width = dom.getTotalWidth(containerDomNode); + height = dom.getTotalHeight(contentsDomNode); + width = dom.getTotalWidth(contentsDomNode); this._contentWidth = width; this._updateMinimumWidth(); this._resizableNode.layout(height, width); diff --git a/src/vs/editor/contrib/hover/browser/hover.css b/src/vs/editor/contrib/hover/browser/hover.css index 958f1ee9cf4..b9da587c918 100644 --- a/src/vs/editor/contrib/hover/browser/hover.css +++ b/src/vs/editor/contrib/hover/browser/hover.css @@ -7,17 +7,15 @@ background-color: var(--vscode-editor-hoverHighlightBackground); } -.monaco-editor .monaco-hover-content { - padding-right: 2px; - padding-bottom: 2px; - box-sizing: border-box; +.monaco-editor .monaco-resizable-hover { + border: 1px solid var(--vscode-editorHoverWidget-border); + border-radius: 3px; + box-sizing: content-box; } .monaco-editor .monaco-hover { color: var(--vscode-editorHoverWidget-foreground); background-color: var(--vscode-editorHoverWidget-background); - border: 1px solid var(--vscode-editorHoverWidget-border); - border-radius: 3px; } .monaco-editor .monaco-hover a { From 17f6bcb7d2d89a03c53319f93b1e677053612d96 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 6 Dec 2024 18:53:29 +0100 Subject: [PATCH 027/479] chore - modernise test runner (#235511) async/wait, remove AMD/ESM split, cleanup --- test/unit/electron/renderer.js | 222 ++++++++++++++++----------------- 1 file changed, 107 insertions(+), 115 deletions(-) diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index 7c28a98930c..b93d91a78e9 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -5,6 +5,8 @@ /*eslint-env mocha*/ +// @ts-check + const fs = require('fs'); (function () { @@ -24,7 +26,7 @@ const fs = require('fs'); function createSpy(element, cnt) { return function (...args) { if (logging) { - console.log(`calling ${element}: ` + args.slice(0, cnt).join(',') + (withStacks ? (`\n` + new Error().stack.split('\n').slice(2).join('\n')) : '')); + console.log(`calling ${element}: ` + args.slice(0, cnt).join(',') + (withStacks ? (`\n` + new Error().stack?.split('\n').slice(2).join('\n')) : '')); } return originals[element].call(this, ...args); }; @@ -88,9 +90,18 @@ Object.assign(globalThis, { const IS_CI = !!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY; const _tests_glob = '**/test/**/*.test.js'; -let loader; + + +/** + * Loads one or N modules. + * @type {{ + * (module: string|string[]): Promise|Promise; + * _out: string; + * }} + */ +let loadFn; + const _loaderErrors = []; -let _out; function initNls(opts) { if (opts.build) { @@ -101,20 +112,17 @@ function initNls(opts) { } } -function initLoader(opts) { +function initLoadFn(opts) { const outdir = opts.build ? 'out-build' : 'out'; - _out = path.join(__dirname, `../../../${outdir}`); + const out = path.join(__dirname, `../../../${outdir}`); const baseUrl = pathToFileURL(path.join(__dirname, `../../../${outdir}/`)); globalThis._VSCODE_FILE_ROOT = baseUrl.href; // set loader - /** - * @param {string[]} modules - * @param {(...args:any[]) => void} callback - */ - function esmRequire(modules, callback, errorback) { - const tasks = modules.map(mod => { + function importModules(modules) { + const moduleArray = Array.isArray(modules) ? modules : [modules]; + const tasks = moduleArray.map(mod => { const url = new URL(`./${mod}.js`, baseUrl).href; return import(url).catch(err => { console.log(mod, url); @@ -124,35 +132,33 @@ function initLoader(opts) { }); }); - Promise.all(tasks).then(modules => callback(...modules)).catch(errorback); + return Array.isArray(modules) + ? Promise.all(tasks) + : tasks[0]; } - - loader = { require: esmRequire }; + importModules._out = out; + loadFn = importModules; } -function createCoverageReport(opts) { - if (opts.coverage) { - return coverage.createReport(opts.run || opts.runGlob); +async function createCoverageReport(opts) { + if (!opts.coverage) { + return undefined; } - return Promise.resolve(undefined); -} - -function loadWorkbenchTestingUtilsModule() { - return new Promise((resolve, reject) => { - loader.require(['vs/workbench/test/common/utils'], resolve, reject); - }); + return coverage.createReport(opts.run || opts.runGlob); } async function loadModules(modules) { for (const file of modules) { mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE, globalThis, file, mocha); - const m = await new Promise((resolve, reject) => loader.require([file], resolve, reject)); + const m = await loadFn(file); mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_REQUIRE, m, file, mocha); mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_POST_REQUIRE, globalThis, file, mocha); } } -function loadTestModules(opts) { +const globAsync = util.promisify(glob); + +async function loadTestModules(opts) { if (opts.run) { const files = Array.isArray(opts.run) ? opts.run : [opts.run]; @@ -164,17 +170,9 @@ function loadTestModules(opts) { } const pattern = opts.runGlob || _tests_glob; - - return new Promise((resolve, reject) => { - glob(pattern, { cwd: _out }, (err, files) => { - if (err) { - reject(err); - return; - } - const modules = files.map(file => file.replace(/\.js$/, '')); - resolve(modules); - }); - }).then(loadModules); + const files = await globAsync(pattern, { cwd: loadFn._out }); + const modules = files.map(file => file.replace(/\.js$/, '')); + return loadModules(modules); } /** @type Mocha.Test */ @@ -220,7 +218,7 @@ async function loadTests(opts) { console[consoleFn.name] = function (msg) { if (!currentTest) { consoleFn.apply(console, arguments); - } else if (!_allowedTestOutput.some(a => a.test(msg)) && !_allowedTestsWithOutput.has(currentTest.title) && !_allowedSuitesWithOutput.has(currentTest.parent?.title)) { + } else if (!_allowedTestOutput.some(a => a.test(msg)) && !_allowedTestsWithOutput.has(currentTest.title) && !_allowedSuitesWithOutput.has(currentTest.parent?.title ?? '')) { _testsWithUnexpectedOutput = true; consoleFn.apply(console, arguments); } @@ -242,79 +240,74 @@ async function loadTests(opts) { 'Search Model: Search reports timed telemetry on search when error is called' ]); - loader.require(['vs/base/common/errors'], function (errors) { - - const onUnexpectedError = function (err) { - if (err.name === 'Canceled') { - return; // ignore canceled errors that are common - } - - let stack = (err ? err.stack : null); - if (!stack) { - stack = new Error().stack; - } + const errors = await loadFn('vs/base/common/errors'); + const onUnexpectedError = function (err) { + if (err.name === 'Canceled') { + return; // ignore canceled errors that are common + } - _unexpectedErrors.push((err && err.message ? err.message : err) + '\n' + stack); - }; + let stack = (err ? err.stack : null); + if (!stack) { + stack = new Error().stack; + } - process.on('uncaughtException', error => onUnexpectedError(error)); - process.on('unhandledRejection', (reason, promise) => { - onUnexpectedError(reason); - promise.catch(() => { }); - }); - window.addEventListener('unhandledrejection', event => { - event.preventDefault(); // Do not log to test output, we show an error later when test ends - event.stopPropagation(); + _unexpectedErrors.push((err && err.message ? err.message : err) + '\n' + stack); + }; - if (!_allowedTestsWithUnhandledRejections.has(currentTest.title)) { - onUnexpectedError(event.reason); - } - }); + process.on('uncaughtException', error => onUnexpectedError(error)); + process.on('unhandledRejection', (reason, promise) => { + onUnexpectedError(reason); + promise.catch(() => { }); + }); + window.addEventListener('unhandledrejection', event => { + event.preventDefault(); // Do not log to test output, we show an error later when test ends + event.stopPropagation(); - errors.setUnexpectedErrorHandler(onUnexpectedError); + if (!_allowedTestsWithUnhandledRejections.has(currentTest.title)) { + onUnexpectedError(event.reason); + } }); + errors.setUnexpectedErrorHandler(onUnexpectedError); //#endregion - return loadWorkbenchTestingUtilsModule().then((workbenchTestingModule) => { - const assertCleanState = workbenchTestingModule.assertCleanState; + const { assertCleanState } = await loadFn('vs/workbench/test/common/utils'); - suite('Tests are using suiteSetup and setup correctly', () => { - test('assertCleanState - check that registries are clean at the start of test running', () => { - assertCleanState(); - }); + suite('Tests are using suiteSetup and setup correctly', () => { + test('assertCleanState - check that registries are clean at the start of test running', () => { + assertCleanState(); }); + }); - setup(async () => { - await perTestCoverage?.startTest(); - }); + setup(async () => { + await perTestCoverage?.startTest(); + }); - teardown(async () => { - await perTestCoverage?.finishTest(currentTest.file, currentTest.fullTitle()); + teardown(async () => { + await perTestCoverage?.finishTest(currentTest.file, currentTest.fullTitle()); - // should not have unexpected output - if (_testsWithUnexpectedOutput && !opts.dev) { - assert.ok(false, 'Error: Unexpected console output in test run. Please ensure no console.[log|error|info|warn] usage in tests or runtime errors.'); - } + // should not have unexpected output + if (_testsWithUnexpectedOutput && !opts.dev) { + assert.ok(false, 'Error: Unexpected console output in test run. Please ensure no console.[log|error|info|warn] usage in tests or runtime errors.'); + } - // should not have unexpected errors - const errors = _unexpectedErrors.concat(_loaderErrors); - if (errors.length) { - for (const error of errors) { - console.error(`Error: Test run should not have unexpected errors:\n${error}`); - } - assert.ok(false, 'Error: Test run should not have unexpected errors.'); + // should not have unexpected errors + const errors = _unexpectedErrors.concat(_loaderErrors); + if (errors.length) { + for (const error of errors) { + console.error(`Error: Test run should not have unexpected errors:\n${error}`); } - }); - - suiteTeardown(() => { // intentionally not in teardown because some tests only cleanup in suiteTeardown + assert.ok(false, 'Error: Test run should not have unexpected errors.'); + } + }); - // should have cleaned up in registries - assertCleanState(); - }); + suiteTeardown(() => { // intentionally not in teardown because some tests only cleanup in suiteTeardown - return loadTestModules(opts); + // should have cleaned up in registries + assertCleanState(); }); + + return loadTestModules(opts); } function serializeSuite(suite) { @@ -403,42 +396,41 @@ class IPCReporter { } } -function runTests(opts) { +async function runTests(opts) { // this *must* come before loadTests, or it doesn't work. if (opts.timeout !== undefined) { mocha.timeout(opts.timeout); } - return loadTests(opts).then(() => { + await loadTests(opts); - if (opts.grep) { - mocha.grep(opts.grep); - } + if (opts.grep) { + mocha.grep(opts.grep); + } - if (!opts.dev) { - mocha.reporter(IPCReporter); - } + if (!opts.dev) { + // @ts-expect-error + mocha.reporter(IPCReporter); + } - const runner = mocha.run(() => { - createCoverageReport(opts).then(() => { - ipcRenderer.send('all done'); - }); - }); + const runner = mocha.run(async () => { + await createCoverageReport(opts) + ipcRenderer.send('all done'); + }); - runner.on('test', test => currentTest = test); + runner.on('test', test => currentTest = test); - if (opts.dev) { - runner.on('fail', (test, err) => { - console.error(test.fullTitle()); - console.error(err.stack); - }); - } - }); + if (opts.dev) { + runner.on('fail', (test, err) => { + console.error(test.fullTitle()); + console.error(err.stack); + }); + } } ipcRenderer.on('run', async (_e, opts) => { initNls(opts); - initLoader(opts); + initLoadFn(opts); await Promise.resolve(globalThis._VSCODE_TEST_INIT); From eb92928b65f9a17b171d5c0e9a906504bcf07c61 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 6 Dec 2024 12:28:32 -0800 Subject: [PATCH 028/479] fix: validate chat session ID before trying to create an editing session from it (#235520) --- .../contrib/chat/browser/chatEditing/chatEditingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts index 34b24c1dbb8..458687df09a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts @@ -157,7 +157,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic void this._editingSessionFileLimitPromise; const sessionIdToRestore = storageService.get(STORAGE_KEY_EDITING_SESSION, StorageScope.WORKSPACE); - if (isString(sessionIdToRestore)) { + if (isString(sessionIdToRestore) && this._chatService.getOrRestoreSession(sessionIdToRestore)) { this._restoringEditingSession = this.startOrContinueEditingSession(sessionIdToRestore); this._restoringEditingSession.finally(() => { this._restoringEditingSession = undefined; From db3873db37f6e1a2d4f3e139573c59c717c015b2 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Fri, 6 Dec 2024 13:03:57 -0800 Subject: [PATCH 029/479] NB multiselect undo/redo fix (#235521) * fix undo/redo during multicursor session * corrected fix -- was backwards initially --- .../multicursor/notebookMulticursor.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts index 2871c2577b1..c8706e4aeb5 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts @@ -385,6 +385,18 @@ export class NotebookMultiCursorController extends Disposable implements INotebo } } + private updateViewModelSelections() { + for (const cell of this.trackedCells) { + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); + if (!controller) { + // should not happen + return; + } + + cell.cellViewModel.setSelections(controller.getSelections()); + } + } + private updateFinalUndoRedo() { const anchorCellModel = this.anchorCell?.[1].getModel(); if (!anchorCellModel) { @@ -775,16 +787,10 @@ export class NotebookMultiCursorController extends Disposable implements INotebo if (model) { models.push(model); } - - const controller = this.cursorsControllers.get(cell.cellViewModel.uri); - if (!controller) { - // should not happen - return; - } - controller.setSelections(new ViewModelEventsCollector(), undefined, cell.cellViewModel.getSelections(), CursorChangeReason.Explicit); } await Promise.all(models.map(model => model.undo())); + this.updateViewModelSelections(); this.updateLazyDecorations(); } @@ -795,16 +801,10 @@ export class NotebookMultiCursorController extends Disposable implements INotebo if (model) { models.push(model); } - - const controller = this.cursorsControllers.get(cell.cellViewModel.uri); - if (!controller) { - // should not happen - return; - } - controller.setSelections(new ViewModelEventsCollector(), undefined, cell.cellViewModel.getSelections(), CursorChangeReason.Explicit); } await Promise.all(models.map(model => model.redo())); + this.updateViewModelSelections(); this.updateLazyDecorations(); } From 62674c8b393ec9d28dd3c777c06f267217bc5a50 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 6 Dec 2024 23:36:11 +0000 Subject: [PATCH 030/479] Require offerSetup set to show CC icon for entitled user (#235530) Require offerSetup set to show CC icon for entitled user (#235518) Fix microsoft/vscode-copilot#11145 --- .../workbench/contrib/chat/browser/actions/chatActions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index cca372c6103..2cbd539c3e4 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -534,9 +534,11 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, { when: ContextKeyExpr.and( ContextKeyExpr.has('config.chat.commandCenter.enabled'), ContextKeyExpr.or( + ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.experimental.offerSetup'), + ChatContextKeys.Setup.entitled, + ), ChatContextKeys.Setup.installed, - ChatContextKeys.Setup.entitled, - ContextKeyExpr.has('config.chat.experimental.offerSetup'), ChatContextKeys.panelParticipantRegistered ) ), From 831c95988ebaf1f83e0459922e699072ce4249d3 Mon Sep 17 00:00:00 2001 From: adrianstephens Date: Fri, 6 Dec 2024 17:12:25 -0800 Subject: [PATCH 031/479] custom editor preview --- src/vs/workbench/browser/parts/editor/editorCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 1cecdaab32f..83f88bd7b74 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -494,7 +494,7 @@ function registerOpenEditorAPICommands(): void { const [columnArg, optionsArg] = columnAndOptions ?? []; - await editorService.openEditor({ resource: URI.from(resource, true), options: { ...optionsArg, pinned: true, override: id } }, columnToEditorGroup(editorGroupsService, configurationService, columnArg)); + await editorService.openEditor({ resource: URI.from(resource, true), options: { pinned: true, ...optionsArg, override: id } }, columnToEditorGroup(editorGroupsService, configurationService, columnArg)); }); // partial, renderer-side API command to open diff editor From bfa5930c7c0af0759d29e1a89a8f942e303fb870 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 7 Dec 2024 12:52:14 +0100 Subject: [PATCH 032/479] chat - assorted fixes (#235539) * Copilot setup fails to focus chat input after setup (microsoft/vscode-copilot#11154) * chat - removal of checkboxes * chat - unbreak :robot: icon from appearing * chat - tweak wording --- .../chat/browser/actions/chatActions.ts | 1 + .../contrib/chat/browser/chatSetup.ts | 87 ++++++------------- .../chat/browser/media/chatViewSetup.css | 17 +--- 3 files changed, 30 insertions(+), 75 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 2cbd539c3e4..fd1f3472c2d 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -538,6 +538,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, { ContextKeyExpr.has('config.chat.experimental.offerSetup'), ChatContextKeys.Setup.entitled, ), + ContextKeyExpr.has('config.chat.experimental.offerSetup'), ChatContextKeys.Setup.installed, ChatContextKeys.panelParticipantRegistered ) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 27c17e06172..43e433d8b66 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import './media/chatViewSetup.css'; -import { $, addDisposableListener, EventType, getActiveElement, setVisibility } from '../../../../base/browser/dom.js'; +import { $, getActiveElement, setVisibility } from '../../../../base/browser/dom.js'; import { Button, ButtonWithDropdown } from '../../../../base/browser/ui/button/button.js'; import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; import { IAction, toAction } from '../../../../base/common/actions.js'; @@ -37,7 +37,7 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import { asText, IRequestService } from '../../../../platform/request/common/request.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { defaultButtonStyles, defaultCheckboxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; +import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; @@ -55,7 +55,6 @@ import { ChatViewId, EditsViewId, ensureSideBarChatViewSize, IChatWidget, showCh import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID } from './chatViewPane.js'; import { ChatViewsWelcomeExtensions, IChatViewsWelcomeContributionRegistry } from './viewsWelcome/chatViewsWelcome.js'; import { IChatQuotasService } from './chatQuotasService.js'; -import { Checkbox } from '../../../../base/browser/ui/toggle/toggle.js'; import { mainWindow } from '../../../../base/browser/window.js'; const defaultChat = { @@ -65,7 +64,6 @@ const defaultChat = { termsStatementUrl: product.defaultChatAgent?.termsStatementUrl ?? '', privacyStatementUrl: product.defaultChatAgent?.privacyStatementUrl ?? '', skusDocumentationUrl: product.defaultChatAgent?.skusDocumentationUrl ?? '', - publicCodeMatchesUrl: product.defaultChatAgent?.publicCodeMatchesUrl ?? '', providerId: product.defaultChatAgent?.providerId ?? '', providerName: product.defaultChatAgent?.providerName ?? '', providerScopes: product.defaultChatAgent?.providerScopes ?? [[]], @@ -88,8 +86,6 @@ enum ChatEntitlement { Pro } -const ASK_FOR_PUBLIC_CODE_MATCHES = false; // TODO@bpasero revisit this - //#region Contribution const TRIGGER_SETUP_COMMAND_ID = 'workbench.action.chat.triggerSetup'; @@ -183,9 +179,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr showCopilotView(viewsService, layoutService); ensureSideBarChatViewSize(400, viewDescriptorService, layoutService); - if (startSetup === true && !ASK_FOR_PUBLIC_CODE_MATCHES) { + if (startSetup === true) { const controller = that.controller.value; - controller.setup({ publicCodeSuggestions: true }); + controller.setup(); } configurationService.updateValue('chat.commandCenter.enabled', true); @@ -521,10 +517,10 @@ class ChatSetupRequests extends Disposable { return this.resolveEntitlement(session, CancellationToken.None); } - async signUpLimited(session: AuthenticationSession, options: { publicCodeSuggestions: boolean }): Promise { + async signUpLimited(session: AuthenticationSession): Promise { const body = { restricted_telemetry: 'disabled', - public_code_suggestions: options.publicCodeSuggestions ? 'enabled' : 'disabled' + public_code_suggestions: 'enabled' }; const response = await this.request(defaultChat.entitlementSignupLimitedUrl, 'POST', body, session, CancellationToken.None); @@ -637,7 +633,7 @@ class ChatSetupController extends Disposable { this._onDidChange.fire(); } - async setup(options: { publicCodeSuggestions: boolean }): Promise { + async setup(): Promise { const title = localize('setupChatProgress', "Getting Copilot ready..."); const badge = this.activityService.showViewContainerActivity(isCopilotEditsViewActive(this.viewsService) ? CHAT_EDITING_SIDEBAR_PANEL_ID : CHAT_SIDEBAR_PANEL_ID, { badge: new ProgressBadge(() => title), @@ -649,14 +645,16 @@ class ChatSetupController extends Disposable { location: ProgressLocation.Window, command: TRIGGER_SETUP_COMMAND_ID, title, - }, () => this.doSetup(options)); + }, () => this.doSetup()); } finally { badge.dispose(); } } - private async doSetup(options: { publicCodeSuggestions: boolean }): Promise { + private async doSetup(): Promise { this.context.suspend(); // reduces flicker + + let focusChatInput = false; try { let session: AuthenticationSession | undefined; let entitlement: ChatEntitlement | undefined; @@ -680,13 +678,22 @@ class ChatSetupController extends Disposable { } } + const activeElement = getActiveElement(); + // Install this.setStep(ChatSetupStep.Installing); - await this.install(session, entitlement ?? this.context.state.entitlement, options); + await this.install(session, entitlement ?? this.context.state.entitlement); + + const currentActiveElement = getActiveElement(); + focusChatInput = activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body; } finally { this.setStep(ChatSetupStep.Initial); this.context.resume(); } + + if (focusChatInput) { + (await showCopilotView(this.viewsService, this.layoutService))?.focusInput(); + } } private async signIn(): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { @@ -694,6 +701,7 @@ class ChatSetupController extends Disposable { let entitlement: ChatEntitlement | undefined; try { showCopilotView(this.viewsService, this.layoutService); + session = await this.authenticationService.createSession(defaultChat.providerId, defaultChat.providerScopes[0]); entitlement = await this.requests.forceResolveEntitlement(session); } catch (error) { @@ -707,9 +715,8 @@ class ChatSetupController extends Disposable { return { session, entitlement }; } - private async install(session: AuthenticationSession, entitlement: ChatEntitlement, options: { publicCodeSuggestions: boolean }): Promise { + private async install(session: AuthenticationSession, entitlement: ChatEntitlement,): Promise { const signedIn = !!session; - const activeElement = getActiveElement(); let installResult: 'installed' | 'cancelled' | 'failedInstall' | undefined = undefined; const wasInstalled = this.context.state.installed; @@ -718,7 +725,7 @@ class ChatSetupController extends Disposable { showCopilotView(this.viewsService, this.layoutService); if (entitlement !== ChatEntitlement.Limited && entitlement !== ChatEntitlement.Pro && entitlement !== ChatEntitlement.Unavailable) { - didSignUp = await this.requests.signUpLimited(session, options); + didSignUp = await this.requests.signUpLimited(session); } await this.extensionsWorkbenchService.install(defaultChat.extensionId, { @@ -747,11 +754,6 @@ class ChatSetupController extends Disposable { } this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult, signedIn }); - - const currentActiveElement = getActiveElement(); - if (activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body) { - (await showCopilotView(this.viewsService, this.layoutService))?.focusInput(); - } } } @@ -802,18 +804,14 @@ class ChatSetupWelcomeContent extends Disposable { } // Limited SKU - const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); + const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) Get 50 chats + 2000 completions per month for free.", defaultChat.skusDocumentationUrl); const limitedSkuHeaderContainer = this.element.appendChild($('p')); limitedSkuHeaderContainer.appendChild(this._register(markdown.render(new MarkdownString(limitedSkuHeader, { isTrusted: true, supportThemeIcons: true }))).element); - const publicCodeSuggestionsLabel = localize('detectionLabel', "Allow code suggestions that [match public code]({0})", defaultChat.publicCodeMatchesUrl); - const { container: publicCodeSuggestionsContainer, checkbox: publicCodeSuggestionsCheckbox } = this.createCheckBox(publicCodeSuggestionsLabel, true, markdown); - // Terms const terms = localize({ key: 'termsLabel', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to our [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); const termsContainer = this.element.appendChild($('p')); termsContainer.classList.add('terms-container'); - termsContainer.classList.toggle('is-standalone', !ASK_FOR_PUBLIC_CODE_MATCHES); termsContainer.appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element); // Setup Button @@ -833,30 +831,13 @@ class ChatSetupWelcomeContent extends Disposable { supportIcons: true, ...defaultButtonStyles })); - this._register(button.onDidClick(() => this.controller.setup({ publicCodeSuggestions: ASK_FOR_PUBLIC_CODE_MATCHES ? publicCodeSuggestionsCheckbox.checked : true }))); + this._register(button.onDidClick(() => this.controller.setup())); // Update based on model state - this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button, [publicCodeSuggestionsContainer], [publicCodeSuggestionsCheckbox]))); + this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button))); } - private createCheckBox(label: string, checked: boolean, markdown: MarkdownRenderer): { container: HTMLElement; checkbox: Checkbox } { - const container = this.element.appendChild($('p.checkbox-container')); - const checkbox = this._register(new Checkbox(label, checked, defaultCheckboxStyles)); - container.appendChild(checkbox.domNode); - - const checkboxLabel = container.appendChild(this._register(markdown.render(new MarkdownString(label, { isTrusted: true, supportThemeIcons: true }), { inline: true, className: 'checkbox-label' })).element); - this._register(addDisposableListener(checkboxLabel, EventType.CLICK, e => { - if (checkbox?.enabled && (e.target as HTMLElement).tagName !== 'A') { - checkbox.checked = !checkbox.checked; - checkbox.focus(); - } - })); - - return { container, checkbox }; - } - - private update(limitedSkuHeaderContainer: HTMLElement, button: Button | ButtonWithDropdown, limitedCheckboxContainers: HTMLElement[], limitedCheckboxes: Checkbox[]): void { - const showLimitedCheckboxes = ASK_FOR_PUBLIC_CODE_MATCHES ? this.context.state.entitlement !== ChatEntitlement.Limited && this.context.state.entitlement !== ChatEntitlement.Pro && this.context.state.entitlement !== ChatEntitlement.Unavailable : false; + private update(limitedSkuHeaderContainer: HTMLElement, button: Button | ButtonWithDropdown): void { let showLimitedSkuHeader: boolean; let buttonLabel: string; @@ -882,27 +863,15 @@ class ChatSetupWelcomeContent extends Disposable { } switch (this.controller.step) { - case ChatSetupStep.Initial: - for (const checkbox of limitedCheckboxes) { - checkbox.enable(); - } - break; case ChatSetupStep.SigningIn: - for (const checkbox of limitedCheckboxes) { - checkbox.disable(); - } buttonLabel = localize('setupChatSignIn', "$(loading~spin) Signing in to {0}...", defaultChat.providerName); break; case ChatSetupStep.Installing: - for (const checkbox of limitedCheckboxes) { - checkbox.disable(); - } buttonLabel = localize('setupChatInstalling', "$(loading~spin) Getting Copilot Ready..."); break; } setVisibility(showLimitedSkuHeader, limitedSkuHeaderContainer); - setVisibility(showLimitedCheckboxes, ...limitedCheckboxContainers); button.label = buttonLabel; button.enabled = this.controller.step === ChatSetupStep.Initial; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css index 9fc75ee6c30..74e06915171 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css @@ -17,7 +17,7 @@ background-color: var(--vscode-chat-requestBackground); } - .terms-container.is-standalone { + .terms-container { padding-top: 5px; } @@ -54,19 +54,4 @@ width: 100%; padding: 4px 7px; } - - /** Checkboxes */ - .checkbox-container { - display: flex; - padding-top: 15px; - } - - .checkbox-label { - flex-basis: fit-content; - cursor: pointer; - } - - .checkbox-label p { - display: inline; - } } From 2e52d7fc4957d3d96334df9bf487dded4fa3be61 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 7 Dec 2024 17:34:20 +0100 Subject: [PATCH 033/479] chat - setup/quota tweaks (#235543) --- .../contrib/chat/browser/actions/chatActions.ts | 5 ----- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 14 +++----------- .../contrib/chat/browser/media/chatViewSetup.css | 2 +- .../contrib/chat/common/chatContextKeys.ts | 1 - 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index fd1f3472c2d..de160c8c263 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -534,10 +534,6 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, { when: ContextKeyExpr.and( ContextKeyExpr.has('config.chat.commandCenter.enabled'), ContextKeyExpr.or( - ContextKeyExpr.and( - ContextKeyExpr.has('config.chat.experimental.offerSetup'), - ChatContextKeys.Setup.entitled, - ), ContextKeyExpr.has('config.chat.experimental.offerSetup'), ChatContextKeys.Setup.installed, ChatContextKeys.panelParticipantRegistered @@ -556,7 +552,6 @@ registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction { ContextKeyExpr.has('config.window.commandCenter'), ContextKeyExpr.or( ChatContextKeys.Setup.installed, - ChatContextKeys.Setup.entitled, ContextKeyExpr.has('config.chat.experimental.offerSetup'), ChatContextKeys.panelParticipantRegistered ) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 43e433d8b66..d31113c0d6b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -154,10 +154,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr f1: true, precondition: ContextKeyExpr.and( ChatContextKeys.Setup.installed.negate(), - ContextKeyExpr.or( - ChatContextKeys.Setup.entitled, - ContextKeyExpr.has('config.chat.experimental.offerSetup') - ) + ContextKeyExpr.has('config.chat.experimental.offerSetup') ), menu: { id: MenuId.ChatCommandCenter, @@ -201,10 +198,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr category: CHAT_CATEGORY, precondition: ContextKeyExpr.and( ChatContextKeys.Setup.installed.negate(), - ContextKeyExpr.or( - ChatContextKeys.Setup.entitled, - ContextKeyExpr.has('config.chat.experimental.offerSetup') - ) + ContextKeyExpr.has('config.chat.experimental.offerSetup') ), menu: { id: MenuId.ChatCommandCenter, @@ -804,7 +798,7 @@ class ChatSetupWelcomeContent extends Disposable { } // Limited SKU - const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) Get 50 chats + 2000 completions per month for free.", defaultChat.skusDocumentationUrl); + const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}) with 50 chat messages and 2000 code completions per month.", defaultChat.skusDocumentationUrl); const limitedSkuHeaderContainer = this.element.appendChild($('p')); limitedSkuHeaderContainer.appendChild(this._register(markdown.render(new MarkdownString(limitedSkuHeader, { isTrusted: true, supportThemeIcons: true }))).element); @@ -895,7 +889,6 @@ class ChatSetupContext extends Disposable { private readonly canSignUpContextKey = ChatContextKeys.Setup.canSignUp.bindTo(this.contextKeyService); private readonly signedOutContextKey = ChatContextKeys.Setup.signedOut.bindTo(this.contextKeyService); - private readonly entitledContextKey = ChatContextKeys.Setup.entitled.bindTo(this.contextKeyService); private readonly limitedContextKey = ChatContextKeys.Setup.limited.bindTo(this.contextKeyService); private readonly triggeredContext = ChatContextKeys.Setup.triggered.bindTo(this.contextKeyService); private readonly installedContext = ChatContextKeys.Setup.installed.bindTo(this.contextKeyService); @@ -1000,7 +993,6 @@ class ChatSetupContext extends Disposable { changed = this.updateContextKey(this.signedOutContextKey, this._state.entitlement === ChatEntitlement.Unknown) || changed; changed = this.updateContextKey(this.canSignUpContextKey, this._state.entitlement === ChatEntitlement.Available) || changed; changed = this.updateContextKey(this.limitedContextKey, this._state.entitlement === ChatEntitlement.Limited) || changed; - changed = this.updateContextKey(this.entitledContextKey, this._state.entitlement === ChatEntitlement.Pro) || changed; changed = this.updateContextKey(this.triggeredContext, !!this._state.triggered) || changed; changed = this.updateContextKey(this.installedContext, !!this._state.installed) || changed; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css index 74e06915171..97a06eac5be 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css @@ -29,7 +29,7 @@ } .chat-feature-container .codicon[class*='codicon-'] { - font-size: 20px; + font-size: 16px; } .codicon[class*='codicon-'] { diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index 8e1f49e7fd3..de13af60b8b 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -45,7 +45,6 @@ export namespace ChatContextKeys { canSignUp: new RawContextKey('chatSetupCanSignUp', false, true), // True when user can sign up to be a chat limited user. signedOut: new RawContextKey('chatSetupSignedOut', false, true), // True when user is signed out. - entitled: new RawContextKey('chatSetupEntitled', false, true), // True when user is a chat entitled user. limited: new RawContextKey('chatSetupLimited', false, true), // True when user is a chat limited user. triggered: new RawContextKey('chatSetupTriggered', false, true), // True when chat setup is triggered. From 37f3474c44ac0a2cfc912c0760a24258cc08ebf0 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Sat, 7 Dec 2024 19:35:28 +0100 Subject: [PATCH 034/479] SCM - adopt IDirtyDiffModelService in various places (#235549) * Use model service for $getDiffInformation() * Use the dirty diff model for actions * Move DirtyDiffModel to avoid cyclic import * Skip quick diffs that are not visible when navigating * Decorators share models * Rename diff.ts to dirtyDiff.ts * Rename dirtyDiff -> dirtyDiffModel * Only send diff information from the SCM quick diff provider --- .../browser/mainThreadDocumentsAndEditors.ts | 2 +- .../api/browser/mainThreadEditors.ts | 15 +- .../mainThreadDocumentsAndEditors.test.ts | 2 +- src/vs/workbench/contrib/scm/browser/diff.ts | 130 ---- .../contrib/scm/browser/dirtyDiffModel.ts | 556 ++++++++++++++++ .../contrib/scm/browser/dirtydiffDecorator.ts | 615 +++--------------- .../contrib/scm/browser/scm.contribution.ts | 2 + 7 files changed, 641 insertions(+), 681 deletions(-) delete mode 100644 src/vs/workbench/contrib/scm/browser/diff.ts create mode 100644 src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index 4f5e72a58f7..992229b91b7 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -32,7 +32,7 @@ import { diffSets, diffMaps } from '../../../base/common/collections.js'; import { IPaneCompositePartService } from '../../services/panecomposite/browser/panecomposite.js'; import { ViewContainerLocation } from '../../common/views.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; -import { IDirtyDiffModelService } from '../../contrib/scm/browser/diff.js'; +import { IDirtyDiffModelService } from '../../contrib/scm/browser/dirtyDiffModel.js'; class TextEditorSnapshot { diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 73d81e729cb..deeecec96f2 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -28,8 +28,7 @@ import { IExtHostContext } from '../../services/extensions/common/extHostCustome import { IEditorControl } from '../../common/editor.js'; import { getCodeEditor, ICodeEditor } from '../../../editor/browser/editorBrowser.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; -import { DirtyDiffContribution } from '../../contrib/scm/browser/dirtydiffDecorator.js'; -import { IDirtyDiffModelService } from '../../contrib/scm/browser/diff.js'; +import { IDirtyDiffModelService } from '../../contrib/scm/browser/dirtyDiffModel.js'; import { autorun, constObservable, derived, derivedOpts, IObservable, observableFromEvent } from '../../../base/common/observable.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { isITextModel } from '../../../editor/common/model.js'; @@ -386,13 +385,15 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { return Promise.resolve(diffEditor.getLineChanges() || []); } - const dirtyDiffContribution = codeEditor.getContribution('editor.contrib.dirtydiff'); - - if (dirtyDiffContribution) { - return Promise.resolve((dirtyDiffContribution as DirtyDiffContribution).getChanges()); + if (!codeEditor.hasModel()) { + return Promise.resolve([]); } - return Promise.resolve([]); + const dirtyDiffModel = this._dirtyDiffModelService.getDirtyDiffModel(codeEditor.getModel().uri); + const scmQuickDiff = dirtyDiffModel?.quickDiffs.find(quickDiff => quickDiff.isSCM); + const scmQuickDiffChanges = dirtyDiffModel?.changes.filter(change => change.label === scmQuickDiff?.label); + + return Promise.resolve(scmQuickDiffChanges?.map(change => change.change) ?? []); } } diff --git a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts index b654ddf0f7c..230fc9d2126 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts @@ -36,7 +36,7 @@ import { LanguageService } from '../../../../editor/common/services/languageServ import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; import { TestLanguageConfigurationService } from '../../../../editor/test/common/modes/testLanguageConfigurationService.js'; import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; -import { IDirtyDiffModelService } from '../../../contrib/scm/browser/diff.js'; +import { IDirtyDiffModelService } from '../../../contrib/scm/browser/dirtyDiffModel.js'; import { ITextEditorDiffInformation } from '../../../../platform/editor/common/editor.js'; suite('MainThreadDocumentsAndEditors', () => { diff --git a/src/vs/workbench/contrib/scm/browser/diff.ts b/src/vs/workbench/contrib/scm/browser/diff.ts deleted file mode 100644 index 0248c6826bd..00000000000 --- a/src/vs/workbench/contrib/scm/browser/diff.ts +++ /dev/null @@ -1,130 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ResourceMap } from '../../../../base/common/map.js'; -import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { DirtyDiffModel } from './dirtydiffDecorator.js'; -import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { autorun, observableFromEvent } from '../../../../base/common/observable.js'; -import { isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; -import { DiffAlgorithmName } from '../../../../editor/common/services/editorWorker.js'; -import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; -import { URI } from '../../../../base/common/uri.js'; - -export const IDirtyDiffModelService = createDecorator('IDirtyDiffModelService'); - -export interface IDirtyDiffModelService { - _serviceBrand: undefined; - - /** - * Returns `undefined` if the editor model is not resolved - * @param uri - */ - getDirtyDiffModel(uri: URI): DirtyDiffModel | undefined; - - /** - * Returns `undefined` if the editor model is not resolved - * @param uri - * @param algorithm - */ - getDiffModel(uri: URI, algorithm: DiffAlgorithmName): DirtyDiffModel | undefined; -} - -export class DirtyDiffModelService extends Disposable implements IDirtyDiffModelService { - _serviceBrand: undefined; - - private readonly _dirtyDiffModels = new ResourceMap(); - private readonly _diffModels = new ResourceMap>(); - - private _visibleTextEditorControls = observableFromEvent( - this.editorService.onDidVisibleEditorsChange, - () => this.editorService.visibleTextEditorControls); - - constructor( - @IEditorService private readonly editorService: IEditorService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextFileService private readonly textFileService: ITextFileService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService - ) { - super(); - - this._register(autorun(reader => { - const visibleTextEditorControls = this._visibleTextEditorControls.read(reader); - - // Dispose dirty diff models for text editors that are not visible - for (const [uri, dirtyDiffModel] of this._dirtyDiffModels) { - const textEditorControl = visibleTextEditorControls - .find(editor => isCodeEditor(editor) && - this.uriIdentityService.extUri.isEqual(editor.getModel()?.uri, uri)); - - if (textEditorControl) { - continue; - } - - dirtyDiffModel.dispose(); - this._dirtyDiffModels.delete(uri); - } - - // Dispose diff models for diff editors that are not visible - for (const [uri, dirtyDiffModel] of this._diffModels) { - const diffEditorControl = visibleTextEditorControls - .find(editor => isDiffEditor(editor) && - this.uriIdentityService.extUri.isEqual(editor.getModel()?.modified.uri, uri)); - - if (diffEditorControl) { - continue; - } - - for (const algorithm of dirtyDiffModel.keys()) { - dirtyDiffModel.get(algorithm)?.dispose(); - dirtyDiffModel.delete(algorithm); - } - this._diffModels.delete(uri); - } - })); - } - - getDirtyDiffModel(uri: URI): DirtyDiffModel | undefined { - let model = this._dirtyDiffModels.get(uri); - if (model) { - return model; - } - - const textFileModel = this.textFileService.files.get(uri); - if (!textFileModel?.isResolved()) { - return undefined; - } - - model = this.instantiationService.createInstance(DirtyDiffModel, textFileModel, undefined); - this._dirtyDiffModels.set(uri, model); - - return model; - } - - getDiffModel(uri: URI, algorithm: DiffAlgorithmName): DirtyDiffModel | undefined { - let model = this._diffModels.get(uri)?.get(algorithm); - if (model) { - return model; - } - - const textFileModel = this.textFileService.files.get(uri); - if (!textFileModel?.isResolved()) { - return undefined; - } - - model = this.instantiationService.createInstance(DirtyDiffModel, textFileModel, algorithm); - if (!this._diffModels.has(uri)) { - this._diffModels.set(uri, new Map()); - } - this._diffModels.get(uri)!.set(algorithm, model); - - return model; - } -} - -registerSingleton(IDirtyDiffModelService, DirtyDiffModelService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts b/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts new file mode 100644 index 00000000000..3b49650a294 --- /dev/null +++ b/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts @@ -0,0 +1,556 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ResourceMap } from '../../../../base/common/map.js'; +import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { EncodingMode, IResolvedTextFileEditorModel, isTextFileEditorModel, ITextFileEditorModel, ITextFileService } from '../../../services/textfile/common/textfiles.js'; +import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { autorun, observableFromEvent } from '../../../../base/common/observable.js'; +import { isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; +import { DiffAlgorithmName, IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModel, shouldSynchronizeModel } from '../../../../editor/common/model.js'; +import { IQuickDiffService, QuickDiff, QuickDiffChange, QuickDiffResult } from '../common/quickDiff.js'; +import { ThrottledDelayer } from '../../../../base/common/async.js'; +import { ISCMRepository, ISCMService } from '../common/scm.js'; +import { sortedDiff, equals } from '../../../../base/common/arrays.js'; +import { onUnexpectedError } from '../../../../base/common/errors.js'; +import { Iterable } from '../../../../base/common/iterator.js'; +import { ISplice } from '../../../../base/common/sequence.js'; +import { DiffState } from '../../../../editor/browser/widget/diffEditor/diffEditorViewModel.js'; +import { toLineChanges } from '../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; +import { LineRangeMapping, lineRangeMappingFromChange } from '../../../../editor/common/diff/rangeMapping.js'; +import { IDiffEditorModel } from '../../../../editor/common/editorCommon.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; +import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; + +export const IDirtyDiffModelService = createDecorator('IDirtyDiffModelService'); + +export interface IDirtyDiffModelService { + _serviceBrand: undefined; + + /** + * Returns `undefined` if the editor model is not resolved + * @param uri + */ + getDirtyDiffModel(uri: URI): DirtyDiffModel | undefined; + + /** + * Returns `undefined` if the editor model is not resolved + * @param uri + * @param algorithm + */ + getDiffModel(uri: URI, algorithm: DiffAlgorithmName): DirtyDiffModel | undefined; +} + +export class DirtyDiffModelService extends Disposable implements IDirtyDiffModelService { + _serviceBrand: undefined; + + private readonly _dirtyDiffModels = new ResourceMap(); + private readonly _diffModels = new ResourceMap>(); + + private _visibleTextEditorControls = observableFromEvent( + this.editorService.onDidVisibleEditorsChange, + () => this.editorService.visibleTextEditorControls); + + constructor( + @IEditorService private readonly editorService: IEditorService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextFileService private readonly textFileService: ITextFileService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + ) { + super(); + + this._register(autorun(reader => { + const visibleTextEditorControls = this._visibleTextEditorControls.read(reader); + + // Dispose dirty diff models for text editors that are not visible + for (const [uri, dirtyDiffModel] of this._dirtyDiffModels) { + const textEditorControl = visibleTextEditorControls + .find(editor => isCodeEditor(editor) && + this.uriIdentityService.extUri.isEqual(editor.getModel()?.uri, uri)); + + if (textEditorControl) { + continue; + } + + dirtyDiffModel.dispose(); + this._dirtyDiffModels.delete(uri); + } + + // Dispose diff models for diff editors that are not visible + for (const [uri, dirtyDiffModel] of this._diffModels) { + const diffEditorControl = visibleTextEditorControls + .find(editor => isDiffEditor(editor) && + this.uriIdentityService.extUri.isEqual(editor.getModel()?.modified.uri, uri)); + + if (diffEditorControl) { + continue; + } + + for (const algorithm of dirtyDiffModel.keys()) { + dirtyDiffModel.get(algorithm)?.dispose(); + dirtyDiffModel.delete(algorithm); + } + this._diffModels.delete(uri); + } + })); + } + + getDirtyDiffModel(uri: URI): DirtyDiffModel | undefined { + let model = this._dirtyDiffModels.get(uri); + if (model) { + return model; + } + + const textFileModel = this.textFileService.files.get(uri); + if (!textFileModel?.isResolved()) { + return undefined; + } + + model = this.instantiationService.createInstance(DirtyDiffModel, textFileModel, undefined); + this._dirtyDiffModels.set(uri, model); + + return model; + } + + getDiffModel(uri: URI, algorithm: DiffAlgorithmName): DirtyDiffModel | undefined { + let model = this._diffModels.get(uri)?.get(algorithm); + if (model) { + return model; + } + + const textFileModel = this.textFileService.files.get(uri); + if (!textFileModel?.isResolved()) { + return undefined; + } + + model = this.instantiationService.createInstance(DirtyDiffModel, textFileModel, algorithm); + if (!this._diffModels.has(uri)) { + this._diffModels.set(uri, new Map()); + } + this._diffModels.get(uri)!.set(algorithm, model); + + return model; + } +} + +export class DirtyDiffModel extends Disposable { + + private _model: ITextFileEditorModel; + + private readonly _originalEditorModels = new ResourceMap(); + private readonly _originalEditorModelsDisposables = this._register(new DisposableStore()); + get originalTextModels(): Iterable { + return Iterable.map(this._originalEditorModels.values(), editorModel => editorModel.textEditorModel); + } + + private _disposed = false; + private _quickDiffs: QuickDiff[] = []; + private _quickDiffsPromise?: Promise; + private _diffDelayer = new ThrottledDelayer(200); + + private readonly _onDidChange = new Emitter<{ changes: QuickDiffChange[]; diff: ISplice[] }>(); + readonly onDidChange: Event<{ changes: QuickDiffChange[]; diff: ISplice[] }> = this._onDidChange.event; + + private _changes: QuickDiffChange[] = []; + get changes(): QuickDiffChange[] { return this._changes; } + + /** + * Map of quick diff name to the index of the change in `this.changes` + */ + private _quickDiffChanges: Map = new Map(); + get quickDiffChanges(): Map { return this._quickDiffChanges; } + + private readonly _repositoryDisposables = new DisposableMap(); + + constructor( + textFileModel: IResolvedTextFileEditorModel, + private readonly algorithm: DiffAlgorithmName | undefined, + @ISCMService private readonly scmService: ISCMService, + @IQuickDiffService private readonly quickDiffService: IQuickDiffService, + @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ITextModelService private readonly textModelResolverService: ITextModelService, + @IChatEditingService private readonly _chatEditingService: IChatEditingService, + @IProgressService private readonly progressService: IProgressService, + ) { + super(); + this._model = textFileModel; + + this._register(textFileModel.textEditorModel.onDidChangeContent(() => this.triggerDiff())); + this._register( + Event.filter(configurationService.onDidChangeConfiguration, + e => e.affectsConfiguration('scm.diffDecorationsIgnoreTrimWhitespace') || e.affectsConfiguration('diffEditor.ignoreTrimWhitespace') + )(this.triggerDiff, this) + ); + this._register(scmService.onDidAddRepository(this.onDidAddRepository, this)); + for (const r of scmService.repositories) { + this.onDidAddRepository(r); + } + + this._register(this._model.onDidChangeEncoding(() => { + this._diffDelayer.cancel(); + this._quickDiffs = []; + this._originalEditorModels.clear(); + this._quickDiffsPromise = undefined; + this.setChanges([], new Map()); + this.triggerDiff(); + })); + + this._register(this.quickDiffService.onDidChangeQuickDiffProviders(() => this.triggerDiff())); + this._register(this._chatEditingService.onDidChangeEditingSession(() => this.triggerDiff())); + this.triggerDiff(); + } + + get quickDiffs(): readonly QuickDiff[] { + return this._quickDiffs; + } + + public getQuickDiffResults(): QuickDiffResult[] { + return this._quickDiffs.map(quickDiff => { + const changes = this.changes + .filter(change => change.label === quickDiff.label); + + return { + label: quickDiff.label, + original: quickDiff.originalResource, + modified: this._model.resource, + changes: changes.map(change => change.change), + changes2: changes.map(change => change.change2) + }; + }); + } + + public getDiffEditorModel(originalUri: URI): IDiffEditorModel | undefined { + const editorModel = this._originalEditorModels.get(originalUri); + return editorModel ? + { + modified: this._model.textEditorModel!, + original: editorModel.textEditorModel + } : undefined; + } + + private onDidAddRepository(repository: ISCMRepository): void { + const disposables = new DisposableStore(); + + disposables.add(repository.provider.onDidChangeResources(this.triggerDiff, this)); + + const onDidRemoveRepository = Event.filter(this.scmService.onDidRemoveRepository, r => r === repository); + disposables.add(onDidRemoveRepository(() => this._repositoryDisposables.deleteAndDispose(repository))); + + this._repositoryDisposables.set(repository, disposables); + + this.triggerDiff(); + } + + private triggerDiff(): void { + if (!this._diffDelayer) { + return; + } + + this._diffDelayer + .trigger(async () => { + const result: { changes: QuickDiffChange[]; mapChanges: Map } | null = await this.diff(); + + const editorModels = Array.from(this._originalEditorModels.values()); + if (!result || this._disposed || this._model.isDisposed() || editorModels.some(editorModel => editorModel.isDisposed())) { + return; // disposed + } + + if (editorModels.every(editorModel => editorModel.textEditorModel.getValueLength() === 0)) { + result.changes = []; + } + + this.setChanges(result.changes, result.mapChanges); + }) + .catch(err => onUnexpectedError(err)); + } + + private setChanges(changes: QuickDiffChange[], mapChanges: Map): void { + const diff = sortedDiff(this.changes, changes, (a, b) => compareChanges(a.change, b.change)); + this._changes = changes; + this._quickDiffChanges = mapChanges; + this._onDidChange.fire({ changes, diff }); + } + + private diff(): Promise<{ changes: QuickDiffChange[]; mapChanges: Map } | null> { + return this.progressService.withProgress({ location: ProgressLocation.Scm, delay: 250 }, async () => { + const originalURIs = await this.getQuickDiffsPromise(); + if (this._disposed || this._model.isDisposed() || (originalURIs.length === 0)) { + return Promise.resolve({ changes: [], mapChanges: new Map() }); // disposed + } + + const filteredToDiffable = originalURIs.filter(quickDiff => this.editorWorkerService.canComputeDirtyDiff(quickDiff.originalResource, this._model.resource)); + if (filteredToDiffable.length === 0) { + return Promise.resolve({ changes: [], mapChanges: new Map() }); // All files are too large + } + + const ignoreTrimWhitespaceSetting = this.configurationService.getValue<'true' | 'false' | 'inherit'>('scm.diffDecorationsIgnoreTrimWhitespace'); + const ignoreTrimWhitespace = ignoreTrimWhitespaceSetting === 'inherit' + ? this.configurationService.getValue('diffEditor.ignoreTrimWhitespace') + : ignoreTrimWhitespaceSetting !== 'false'; + + const allDiffs: QuickDiffChange[] = []; + for (const quickDiff of filteredToDiffable) { + const dirtyDiff = await this._diff(quickDiff.originalResource, this._model.resource, ignoreTrimWhitespace); + if (dirtyDiff.changes && dirtyDiff.changes2 && dirtyDiff.changes.length === dirtyDiff.changes2.length) { + for (let index = 0; index < dirtyDiff.changes.length; index++) { + allDiffs.push({ + label: quickDiff.label, + original: quickDiff.originalResource, + modified: this._model.resource, + change: dirtyDiff.changes[index], + change2: dirtyDiff.changes2[index] + }); + } + } + } + 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, []); + } + map.get(label)!.push(i); + } + return { changes: sorted, mapChanges: map }; + }); + } + + private async _diff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<{ changes: readonly IChange[] | null; changes2: readonly LineRangeMapping[] | null }> { + if (this.algorithm === undefined) { + const changes = await this.editorWorkerService.computeDirtyDiff(original, modified, ignoreTrimWhitespace); + return { changes, changes2: changes?.map(change => lineRangeMappingFromChange(change)) ?? null }; + } + + const result = await this.editorWorkerService.computeDiff(original, modified, { + computeMoves: false, + ignoreTrimWhitespace, + maxComputationTimeMs: Number.MAX_SAFE_INTEGER + }, this.algorithm); + + return { changes: result ? toLineChanges(DiffState.fromDiffResult(result)) : null, changes2: result?.changes ?? null }; + } + + private getQuickDiffsPromise(): Promise { + if (this._quickDiffsPromise) { + return this._quickDiffsPromise; + } + + this._quickDiffsPromise = this.getOriginalResource().then(async (quickDiffs) => { + if (this._disposed) { // disposed + return []; + } + + if (quickDiffs.length === 0) { + this._quickDiffs = []; + this._originalEditorModels.clear(); + return []; + } + + if (equals(this._quickDiffs, quickDiffs, (a, b) => a.originalResource.toString() === b.originalResource.toString() && a.label === b.label)) { + return quickDiffs; + } + + this._quickDiffs = quickDiffs; + + this._originalEditorModels.clear(); + this._originalEditorModelsDisposables.clear(); + return (await Promise.all(quickDiffs.map(async (quickDiff) => { + try { + const ref = await this.textModelResolverService.createModelReference(quickDiff.originalResource); + if (this._disposed) { // disposed + ref.dispose(); + return []; + } + + this._originalEditorModels.set(quickDiff.originalResource, ref.object); + + if (isTextFileEditorModel(ref.object)) { + const encoding = this._model.getEncoding(); + + if (encoding) { + ref.object.setEncoding(encoding, EncodingMode.Decode); + } + } + + this._originalEditorModelsDisposables.add(ref); + this._originalEditorModelsDisposables.add(ref.object.textEditorModel.onDidChangeContent(() => this.triggerDiff())); + + return quickDiff; + } catch (error) { + return []; // possibly invalid reference + } + }))).flat(); + }); + + return this._quickDiffsPromise.finally(() => { + this._quickDiffsPromise = undefined; + }); + } + + private async getOriginalResource(): Promise { + if (this._disposed) { + return Promise.resolve([]); + } + const uri = this._model.resource; + + const session = this._chatEditingService.currentEditingSession; + if (session && session.getEntry(uri)?.state.get() === WorkingSetEntryState.Modified) { + // disable dirty diff when doing chat edits + return Promise.resolve([]); + } + + const isSynchronized = this._model.textEditorModel ? shouldSynchronizeModel(this._model.textEditorModel) : undefined; + const quickDiffs = await this.quickDiffService.getQuickDiffs(uri, this._model.getLanguageId(), isSynchronized); + + // TODO@lszomoru - find a long term solution for this + // When the DirtyDiffModel is created for a diff editor, there is no + // need to compute the diff information for the `isSCM` quick diff + // provider as that information will be provided by the diff editor + return this.algorithm !== undefined + ? quickDiffs.filter(quickDiff => !quickDiff.isSCM) + : quickDiffs; + } + + findNextClosestChange(lineNumber: number, inclusive = true, provider?: string): number { + let preferredProvider: string | undefined; + if (!provider && inclusive) { + preferredProvider = this.quickDiffs.find(value => value.isSCM)?.label; + } + + const possibleChanges: number[] = []; + for (let i = 0; i < this.changes.length; i++) { + if (provider && this.changes[i].label !== provider) { + continue; + } + + // Skip quick diffs that are not visible + if (!this.quickDiffs.find(quickDiff => quickDiff.label === this.changes[i].label)?.visible) { + continue; + } + + const change = this.changes[i]; + const possibleChangesLength = possibleChanges.length; + + if (inclusive) { + if (getModifiedEndLineNumber(change.change) >= lineNumber) { + if (preferredProvider && change.label !== preferredProvider) { + possibleChanges.push(i); + } else { + return i; + } + } + } else { + if (change.change.modifiedStartLineNumber > lineNumber) { + return i; + } + } + if ((possibleChanges.length > 0) && (possibleChanges.length === possibleChangesLength)) { + return possibleChanges[0]; + } + } + + return possibleChanges.length > 0 ? possibleChanges[0] : 0; + } + + findPreviousClosestChange(lineNumber: number, inclusive = true, provider?: string): number { + for (let i = this.changes.length - 1; i >= 0; i--) { + if (provider && this.changes[i].label !== provider) { + continue; + } + + // Skip quick diffs that are not visible + if (!this.quickDiffs.find(quickDiff => quickDiff.label === this.changes[i].label)?.visible) { + continue; + } + + const change = this.changes[i].change; + + if (inclusive) { + if (change.modifiedStartLineNumber <= lineNumber) { + return i; + } + } else { + if (getModifiedEndLineNumber(change) < lineNumber) { + return i; + } + } + } + + return this.changes.length - 1; + } + + override dispose(): void { + this._disposed = true; + + this._quickDiffs = []; + this._diffDelayer.cancel(); + this._originalEditorModels.clear(); + this._repositoryDisposables.dispose(); + + super.dispose(); + } +} + +function compareChanges(a: IChange, b: IChange): number { + let result = a.modifiedStartLineNumber - b.modifiedStartLineNumber; + + if (result !== 0) { + return result; + } + + result = a.modifiedEndLineNumber - b.modifiedEndLineNumber; + + if (result !== 0) { + return result; + } + + result = a.originalStartLineNumber - b.originalStartLineNumber; + + if (result !== 0) { + return result; + } + + return a.originalEndLineNumber - b.originalEndLineNumber; +} + +export function getChangeHeight(change: IChange): number { + const modified = change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1; + const original = change.originalEndLineNumber - change.originalStartLineNumber + 1; + + if (change.originalEndLineNumber === 0) { + return modified; + } else if (change.modifiedEndLineNumber === 0) { + return original; + } else { + return modified + original; + } +} + +export function getModifiedEndLineNumber(change: IChange): number { + if (change.modifiedEndLineNumber === 0) { + return change.modifiedStartLineNumber === 0 ? 1 : change.modifiedStartLineNumber; + } else { + return change.modifiedEndLineNumber; + } +} + +export function lineIntersectsChange(lineNumber: number, change: IChange): boolean { + // deletion at the beginning of the file + if (lineNumber === 1 && change.modifiedStartLineNumber === 0 && change.modifiedEndLineNumber === 0) { + return true; + } + + return lineNumber >= change.modifiedStartLineNumber && lineNumber <= (change.modifiedEndLineNumber || change.modifiedStartLineNumber); +} diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 7ca28f94265..473c2f5d418 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -6,16 +6,11 @@ import * as nls from '../../../../nls.js'; import './media/dirtydiffDecorator.css'; -import { ThrottledDelayer } from '../../../../base/common/async.js'; -import { IDisposable, dispose, toDisposable, Disposable, DisposableStore, DisposableMap } from '../../../../base/common/lifecycle.js'; -import { Event, Emitter } from '../../../../base/common/event.js'; -import * as ext from '../../../common/contributions.js'; +import { IDisposable, toDisposable, Disposable, DisposableStore, DisposableMap } from '../../../../base/common/lifecycle.js'; +import { Event } from '../../../../base/common/event.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { DiffAlgorithmName, IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { URI } from '../../../../base/common/uri.js'; -import { ISCMService, ISCMRepository } from '../common/scm.js'; import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; import { IColorTheme, themeColorFromId, IThemeService } from '../../../../platform/theme/common/themeService.js'; import { editorErrorForeground, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; @@ -37,33 +32,28 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { basename } from '../../../../base/common/resources.js'; import { MenuId, IMenuService, IMenu, MenuItemAction, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { getFlatActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; -import { ScrollType, IEditorContribution, IDiffEditorModel, IEditorModel, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { OverviewRulerLane, ITextModel, IModelDecorationOptions, MinimapPosition, shouldSynchronizeModel } from '../../../../editor/common/model.js'; -import { equals, sortedDiff } from '../../../../base/common/arrays.js'; +import { ScrollType, IEditorContribution, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; +import { OverviewRulerLane, IModelDecorationOptions, MinimapPosition } from '../../../../editor/common/model.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { ISplice } from '../../../../base/common/sequence.js'; import * as dom from '../../../../base/browser/dom.js'; import * as domStylesheetsJs from '../../../../base/browser/domStylesheets.js'; -import { EncodingMode, ITextFileEditorModel, IResolvedTextFileEditorModel, ITextFileService, isTextFileEditorModel } from '../../../services/textfile/common/textfiles.js'; import { gotoNextLocation, gotoPreviousLocation } from '../../../../platform/theme/common/iconRegistry.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; -import { onUnexpectedError } from '../../../../base/common/errors.js'; import { TextCompareEditorActiveContext } from '../../../common/contextkeys.js'; -import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; import { Color } from '../../../../base/common/color.js'; -import { ResourceMap } from '../../../../base/common/map.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; -import { IQuickDiffService, QuickDiff, QuickDiffChange, QuickDiffResult } from '../common/quickDiff.js'; +import { IQuickDiffService, QuickDiffChange } from '../common/quickDiff.js'; import { IQuickDiffSelectItem, SwitchQuickDiffBaseAction, SwitchQuickDiffViewItem } from './dirtyDiffSwitcher.js'; -import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; -import { LineRangeMapping, lineRangeMappingFromChange } from '../../../../editor/common/diff/rangeMapping.js'; -import { DiffState } from '../../../../editor/browser/widget/diffEditor/diffEditorViewModel.js'; -import { toLineChanges } from '../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; import { Iterable } from '../../../../base/common/iterator.js'; +import { DirtyDiffModel, getChangeHeight, getModifiedEndLineNumber, IDirtyDiffModelService, lineIntersectsChange } from './dirtyDiffModel.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { ResourceMap } from '../../../../base/common/map.js'; +import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; class DiffActionRunner extends ActionRunner { @@ -76,46 +66,8 @@ class DiffActionRunner extends ActionRunner { } } -export interface IModelRegistry { - getModel(editorModel: IEditorModel, codeEditor: ICodeEditor): DirtyDiffModel | undefined; -} - -export interface DirtyDiffContribution extends IEditorContribution { - getChanges(): IChange[]; -} - export const isDirtyDiffVisible = new RawContextKey('dirtyDiffVisible', false); -function getChangeHeight(change: IChange): number { - const modified = change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1; - const original = change.originalEndLineNumber - change.originalStartLineNumber + 1; - - if (change.originalEndLineNumber === 0) { - return modified; - } else if (change.modifiedEndLineNumber === 0) { - return original; - } else { - return modified + original; - } -} - -function getModifiedEndLineNumber(change: IChange): number { - if (change.modifiedEndLineNumber === 0) { - return change.modifiedStartLineNumber === 0 ? 1 : change.modifiedStartLineNumber; - } else { - return change.modifiedEndLineNumber; - } -} - -function lineIntersectsChange(lineNumber: number, change: IChange): boolean { - // deletion at the beginning of the file - if (lineNumber === 1 && change.modifiedStartLineNumber === 0 && change.modifiedEndLineNumber === 0) { - return true; - } - - return lineNumber >= change.modifiedStartLineNumber && lineNumber <= (change.modifiedEndLineNumber || change.modifiedStartLineNumber); -} - class UIEditorAction extends Action { private editor: ICodeEditor; @@ -575,23 +527,18 @@ export class GotoPreviousChangeAction extends EditorAction { const accessibilitySignalService = accessor.get(IAccessibilitySignalService); const accessibilityService = accessor.get(IAccessibilityService); const codeEditorService = accessor.get(ICodeEditorService); + const dirtyDiffModelService = accessor.get(IDirtyDiffModelService); if (!outerEditor || !outerEditor.hasModel()) { return; } - const controller = DirtyDiffController.get(outerEditor); - - if (!controller || !controller.modelRegistry) { - return; - } - - const lineNumber = outerEditor.getPosition().lineNumber; - const model = controller.modelRegistry.getModel(outerEditor.getModel(), outerEditor); + const model = dirtyDiffModelService.getDirtyDiffModel(outerEditor.getModel().uri); if (!model || model.changes.length === 0) { return; } + const lineNumber = outerEditor.getPosition().lineNumber; const index = model.findPreviousClosestChange(lineNumber, false); const change = model.changes[index]; await playAccessibilitySymbolForChange(change.change, accessibilitySignalService); @@ -616,24 +563,19 @@ export class GotoNextChangeAction extends EditorAction { const outerEditor = getOuterEditorFromDiffEditor(accessor); const accessibilityService = accessor.get(IAccessibilityService); const codeEditorService = accessor.get(ICodeEditorService); + const dirtyDiffModelService = accessor.get(IDirtyDiffModelService); if (!outerEditor || !outerEditor.hasModel()) { return; } - const controller = DirtyDiffController.get(outerEditor); - - if (!controller || !controller.modelRegistry) { - return; - } - - const lineNumber = outerEditor.getPosition().lineNumber; - const model = controller.modelRegistry.getModel(outerEditor.getModel(), outerEditor); + const model = dirtyDiffModelService.getDirtyDiffModel(outerEditor.getModel().uri); if (!model || model.changes.length === 0) { return; } + const lineNumber = outerEditor.getPosition().lineNumber; const index = model.findNextClosestChange(lineNumber, false); const change = model.changes[index].change; await playAccessibilitySymbolForChange(change, accessibilitySignalService); @@ -691,7 +633,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -export class DirtyDiffController extends Disposable implements DirtyDiffContribution { +export class DirtyDiffController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.dirtydiff'; @@ -699,8 +641,6 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu return editor.getContribution(DirtyDiffController.ID); } - modelRegistry: IModelRegistry | null = null; - private model: DirtyDiffModel | null = null; private widget: DirtyDiffWidget | null = null; private readonly isDirtyDiffVisible!: IContextKey; @@ -714,6 +654,7 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu private editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, + @IDirtyDiffModelService private readonly dirtyDiffModelService: IDirtyDiffModelService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -827,17 +768,13 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu return true; } - if (!this.modelRegistry) { - return false; - } - const editorModel = this.editor.getModel(); if (!editorModel) { return false; } - const model = this.modelRegistry.getModel(editorModel, this.editor); + const model = this.dirtyDiffModelService.getDirtyDiffModel(editorModel.uri); if (!model) { return false; @@ -940,17 +877,13 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu return; } - if (!this.modelRegistry) { - return; - } - const editorModel = this.editor.getModel(); if (!editorModel) { return; } - const model = this.modelRegistry.getModel(editorModel, this.editor); + const model = this.dirtyDiffModelService.getDirtyDiffModel(editorModel.uri); if (!model) { return; @@ -969,23 +902,6 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu } } - getChanges(): IChange[] { - if (!this.modelRegistry) { - return []; - } - if (!this.editor.hasModel()) { - return []; - } - - const model = this.modelRegistry.getModel(this.editor.getModel(), this.editor); - - if (!model) { - return []; - } - - return model.changes.map(change => change.change); - } - override dispose(): void { this.gutterActionDisposables.dispose(); super.dispose(); @@ -1054,16 +970,13 @@ class DirtyDiffDecorator extends Disposable { private modifiedPatternOptions: ModelDecorationOptions; private deletedOptions: ModelDecorationOptions; private decorationsCollection: IEditorDecorationsCollection | undefined; - private editorModel: ITextModel | null; constructor( - editorModel: ITextModel, private readonly codeEditor: ICodeEditor, - private model: DirtyDiffModel, + private readonly dirtyDiffModel: DirtyDiffModel, @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); - this.editorModel = editorModel; const decorations = configurationService.getValue('scm.diffDecorations'); const gutter = decorations === 'all' || decorations === 'gutter'; @@ -1109,18 +1022,18 @@ class DirtyDiffDecorator extends Disposable { } })); - this._register(model.onDidChange(this.onDidChange, this)); + this._register(Event.runAndSubscribe(dirtyDiffModel.onDidChange, () => this.onDidChange())); } private onDidChange(): void { - if (!this.editorModel) { + if (!this.codeEditor.hasModel()) { return; } - const visibleQuickDiffs = this.model.quickDiffs.filter(quickDiff => quickDiff.visible); + const visibleQuickDiffs = this.dirtyDiffModel.quickDiffs.filter(quickDiff => quickDiff.visible); const pattern = this.configurationService.getValue<{ added: boolean; modified: boolean }>('scm.diffDecorationsGutterPattern'); - const decorations = this.model.changes + const decorations = this.dirtyDiffModel.changes .filter(labeledChange => visibleQuickDiffs.some(quickDiff => quickDiff.label === labeledChange.label)) .map((labeledChange) => { const change = labeledChange.change; @@ -1164,424 +1077,40 @@ class DirtyDiffDecorator extends Disposable { } override dispose(): void { - super.dispose(); - if (this.decorationsCollection) { this.decorationsCollection?.clear(); } - this.editorModel = null; this.decorationsCollection = undefined; + super.dispose(); } } -function compareChanges(a: IChange, b: IChange): number { - let result = a.modifiedStartLineNumber - b.modifiedStartLineNumber; - - if (result !== 0) { - return result; - } - - result = a.modifiedEndLineNumber - b.modifiedEndLineNumber; - - if (result !== 0) { - return result; - } - - result = a.originalStartLineNumber - b.originalStartLineNumber; - - if (result !== 0) { - return result; - } - - return a.originalEndLineNumber - b.originalEndLineNumber; -} - - export async function getOriginalResource(quickDiffService: IQuickDiffService, uri: URI, language: string | undefined, isSynchronized: boolean | undefined): Promise { const quickDiffs = await quickDiffService.getQuickDiffs(uri, language, isSynchronized); return quickDiffs.length > 0 ? quickDiffs[0].originalResource : null; } -export class DirtyDiffModel extends Disposable { - - private _model: ITextFileEditorModel; - - private readonly _originalEditorModels = new ResourceMap(); - private readonly _originalEditorModelsDisposables = this._register(new DisposableStore()); - get originalTextModels(): Iterable { - return Iterable.map(this._originalEditorModels.values(), editorModel => editorModel.textEditorModel); - } - - private _disposed = false; - private _quickDiffs: QuickDiff[] = []; - private _quickDiffsPromise?: Promise; - private _diffDelayer = new ThrottledDelayer(200); - - private readonly _onDidChange = new Emitter<{ changes: QuickDiffChange[]; diff: ISplice[] }>(); - readonly onDidChange: Event<{ changes: QuickDiffChange[]; diff: ISplice[] }> = this._onDidChange.event; - - private _changes: QuickDiffChange[] = []; - get changes(): QuickDiffChange[] { return this._changes; } - - /** - * Map of quick diff name to the index of the change in `this.changes` - */ - private _quickDiffChanges: Map = new Map(); - get quickDiffChanges(): Map { return this._quickDiffChanges; } - - private readonly _repositoryDisposables = new DisposableMap(); - - constructor( - textFileModel: IResolvedTextFileEditorModel, - private readonly algorithm: DiffAlgorithmName | undefined, - @ISCMService private readonly scmService: ISCMService, - @IQuickDiffService private readonly quickDiffService: IQuickDiffService, - @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ITextModelService private readonly textModelResolverService: ITextModelService, - @IChatEditingService private readonly _chatEditingService: IChatEditingService, - @IProgressService private readonly progressService: IProgressService, - ) { - super(); - this._model = textFileModel; - - this._register(textFileModel.textEditorModel.onDidChangeContent(() => this.triggerDiff())); - this._register( - Event.filter(configurationService.onDidChangeConfiguration, - e => e.affectsConfiguration('scm.diffDecorationsIgnoreTrimWhitespace') || e.affectsConfiguration('diffEditor.ignoreTrimWhitespace') - )(this.triggerDiff, this) - ); - this._register(scmService.onDidAddRepository(this.onDidAddRepository, this)); - for (const r of scmService.repositories) { - this.onDidAddRepository(r); - } - - this._register(this._model.onDidChangeEncoding(() => { - this._diffDelayer.cancel(); - this._quickDiffs = []; - this._originalEditorModels.clear(); - this._quickDiffsPromise = undefined; - this.setChanges([], new Map()); - this.triggerDiff(); - })); - - this._register(this.quickDiffService.onDidChangeQuickDiffProviders(() => this.triggerDiff())); - this._register(this._chatEditingService.onDidChangeEditingSession(() => this.triggerDiff())); - this.triggerDiff(); - } - - get quickDiffs(): readonly QuickDiff[] { - return this._quickDiffs; - } - - public getQuickDiffResults(): QuickDiffResult[] { - return this._quickDiffs.map(quickDiff => { - const changes = this.changes - .filter(change => change.label === quickDiff.label); - - return { - label: quickDiff.label, - original: quickDiff.originalResource, - modified: this._model.resource, - changes: changes.map(change => change.change), - changes2: changes.map(change => change.change2) - }; - }); - } - - public getDiffEditorModel(originalUri: URI): IDiffEditorModel | undefined { - const editorModel = this._originalEditorModels.get(originalUri); - return editorModel ? - { - modified: this._model.textEditorModel!, - original: editorModel.textEditorModel - } : undefined; - } - - private onDidAddRepository(repository: ISCMRepository): void { - const disposables = new DisposableStore(); - - disposables.add(repository.provider.onDidChangeResources(this.triggerDiff, this)); - - const onDidRemoveRepository = Event.filter(this.scmService.onDidRemoveRepository, r => r === repository); - disposables.add(onDidRemoveRepository(() => this._repositoryDisposables.deleteAndDispose(repository))); - - this._repositoryDisposables.set(repository, disposables); - - this.triggerDiff(); - } - - private triggerDiff(): void { - if (!this._diffDelayer) { - return; - } - - this._diffDelayer - .trigger(async () => { - const result: { changes: QuickDiffChange[]; mapChanges: Map } | null = await this.diff(); - - const editorModels = Array.from(this._originalEditorModels.values()); - if (!result || this._disposed || this._model.isDisposed() || editorModels.some(editorModel => editorModel.isDisposed())) { - return; // disposed - } - - if (editorModels.every(editorModel => editorModel.textEditorModel.getValueLength() === 0)) { - result.changes = []; - } - - this.setChanges(result.changes, result.mapChanges); - }) - .catch(err => onUnexpectedError(err)); - } - - private setChanges(changes: QuickDiffChange[], mapChanges: Map): void { - const diff = sortedDiff(this.changes, changes, (a, b) => compareChanges(a.change, b.change)); - this._changes = changes; - this._quickDiffChanges = mapChanges; - this._onDidChange.fire({ changes, diff }); - } - - private diff(): Promise<{ changes: QuickDiffChange[]; mapChanges: Map } | null> { - return this.progressService.withProgress({ location: ProgressLocation.Scm, delay: 250 }, async () => { - const originalURIs = await this.getQuickDiffsPromise(); - if (this._disposed || this._model.isDisposed() || (originalURIs.length === 0)) { - return Promise.resolve({ changes: [], mapChanges: new Map() }); // disposed - } - - const filteredToDiffable = originalURIs.filter(quickDiff => this.editorWorkerService.canComputeDirtyDiff(quickDiff.originalResource, this._model.resource)); - if (filteredToDiffable.length === 0) { - return Promise.resolve({ changes: [], mapChanges: new Map() }); // All files are too large - } - - const ignoreTrimWhitespaceSetting = this.configurationService.getValue<'true' | 'false' | 'inherit'>('scm.diffDecorationsIgnoreTrimWhitespace'); - const ignoreTrimWhitespace = ignoreTrimWhitespaceSetting === 'inherit' - ? this.configurationService.getValue('diffEditor.ignoreTrimWhitespace') - : ignoreTrimWhitespaceSetting !== 'false'; - - const allDiffs: QuickDiffChange[] = []; - for (const quickDiff of filteredToDiffable) { - const dirtyDiff = await this._diff(quickDiff.originalResource, this._model.resource, ignoreTrimWhitespace); - if (dirtyDiff.changes && dirtyDiff.changes2 && dirtyDiff.changes.length === dirtyDiff.changes2.length) { - for (let index = 0; index < dirtyDiff.changes.length; index++) { - allDiffs.push({ - label: quickDiff.label, - original: quickDiff.originalResource, - modified: this._model.resource, - change: dirtyDiff.changes[index], - change2: dirtyDiff.changes2[index] - }); - } - } - } - 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, []); - } - map.get(label)!.push(i); - } - return { changes: sorted, mapChanges: map }; - }); - } - - private async _diff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<{ changes: readonly IChange[] | null; changes2: readonly LineRangeMapping[] | null }> { - if (this.algorithm === undefined) { - const changes = await this.editorWorkerService.computeDirtyDiff(original, modified, ignoreTrimWhitespace); - return { changes, changes2: changes?.map(change => lineRangeMappingFromChange(change)) ?? null }; - } - - const result = await this.editorWorkerService.computeDiff(original, modified, { - computeMoves: false, - ignoreTrimWhitespace, - maxComputationTimeMs: Number.MAX_SAFE_INTEGER - }, this.algorithm); - - return { changes: result ? toLineChanges(DiffState.fromDiffResult(result)) : null, changes2: result?.changes ?? null }; - } - - private getQuickDiffsPromise(): Promise { - if (this._quickDiffsPromise) { - return this._quickDiffsPromise; - } - - this._quickDiffsPromise = this.getOriginalResource().then(async (quickDiffs) => { - if (this._disposed) { // disposed - return []; - } - - if (quickDiffs.length === 0) { - this._quickDiffs = []; - this._originalEditorModels.clear(); - return []; - } - - if (equals(this._quickDiffs, quickDiffs, (a, b) => a.originalResource.toString() === b.originalResource.toString() && a.label === b.label)) { - return quickDiffs; - } - - this._quickDiffs = quickDiffs; - - this._originalEditorModels.clear(); - this._originalEditorModelsDisposables.clear(); - return (await Promise.all(quickDiffs.map(async (quickDiff) => { - try { - const ref = await this.textModelResolverService.createModelReference(quickDiff.originalResource); - if (this._disposed) { // disposed - ref.dispose(); - return []; - } - - this._originalEditorModels.set(quickDiff.originalResource, ref.object); - - if (isTextFileEditorModel(ref.object)) { - const encoding = this._model.getEncoding(); - - if (encoding) { - ref.object.setEncoding(encoding, EncodingMode.Decode); - } - } - - this._originalEditorModelsDisposables.add(ref); - this._originalEditorModelsDisposables.add(ref.object.textEditorModel.onDidChangeContent(() => this.triggerDiff())); - - return quickDiff; - } catch (error) { - return []; // possibly invalid reference - } - }))).flat(); - }); - - return this._quickDiffsPromise.finally(() => { - this._quickDiffsPromise = undefined; - }); - } - - private async getOriginalResource(): Promise { - if (this._disposed) { - return Promise.resolve([]); - } - const uri = this._model.resource; - - const session = this._chatEditingService.currentEditingSession; - if (session && session.getEntry(uri)?.state.get() === WorkingSetEntryState.Modified) { - // disable dirty diff when doing chat edits - return Promise.resolve([]); - } - - const isSynchronized = this._model.textEditorModel ? shouldSynchronizeModel(this._model.textEditorModel) : undefined; - const quickDiffs = await this.quickDiffService.getQuickDiffs(uri, this._model.getLanguageId(), isSynchronized); - - // TODO@lszomoru - find a long term solution for this - // When the DirtyDiffModel is created for a diff editor, there is no - // need to compute the diff information for the `isSCM` quick diff - // provider as that information will be provided by the diff editor - return this.algorithm !== undefined - ? quickDiffs.filter(quickDiff => !quickDiff.isSCM) - : quickDiffs; - } - - findNextClosestChange(lineNumber: number, inclusive = true, provider?: string): number { - let preferredProvider: string | undefined; - if (!provider && inclusive) { - preferredProvider = this.quickDiffs.find(value => value.isSCM)?.label; - } - - const possibleChanges: number[] = []; - for (let i = 0; i < this.changes.length; i++) { - if (provider && this.changes[i].label !== provider) { - continue; - } - const change = this.changes[i]; - const possibleChangesLength = possibleChanges.length; - - if (inclusive) { - if (getModifiedEndLineNumber(change.change) >= lineNumber) { - if (preferredProvider && change.label !== preferredProvider) { - possibleChanges.push(i); - } else { - return i; - } - } - } else { - if (change.change.modifiedStartLineNumber > lineNumber) { - return i; - } - } - if ((possibleChanges.length > 0) && (possibleChanges.length === possibleChangesLength)) { - return possibleChanges[0]; - } - } - - return possibleChanges.length > 0 ? possibleChanges[0] : 0; - } - - findPreviousClosestChange(lineNumber: number, inclusive = true, provider?: string): number { - for (let i = this.changes.length - 1; i >= 0; i--) { - if (provider && this.changes[i].label !== provider) { - continue; - } - const change = this.changes[i].change; - - if (inclusive) { - if (change.modifiedStartLineNumber <= lineNumber) { - return i; - } - } else { - if (getModifiedEndLineNumber(change) < lineNumber) { - return i; - } - } - } - - return this.changes.length - 1; - } - - override dispose(): void { - this._disposed = true; - - this._quickDiffs = []; - this._diffDelayer.cancel(); - this._originalEditorModels.clear(); - this._repositoryDisposables.dispose(); - - super.dispose(); - } -} - -class DirtyDiffItem { - - constructor( - readonly model: DirtyDiffModel, - readonly decorator: DirtyDiffDecorator - ) { } - - dispose(): void { - this.decorator.dispose(); - this.model.dispose(); - } -} - -interface IViewState { +interface DirtyDiffWorkbenchControllerViewState { readonly width: number; readonly visibility: 'always' | 'hover'; } -export class DirtyDiffWorkbenchController extends Disposable implements ext.IWorkbenchContribution, IModelRegistry { +export class DirtyDiffWorkbenchController extends Disposable implements IWorkbenchContribution { private enabled = false; - private viewState: IViewState = { width: 3, visibility: 'always' }; - private items = new ResourceMap>(); // resource -> editor id -> DirtyDiffItem + + // Resource URI -> Code Editor Id -> Decoration (Disposable) + private readonly decorators = new ResourceMap>(); + private viewState: DirtyDiffWorkbenchControllerViewState = { width: 3, visibility: 'always' }; private readonly transientDisposables = this._register(new DisposableStore()); - private stylesheet: HTMLStyleElement; + private readonly stylesheet: HTMLStyleElement; constructor( @IEditorService private readonly editorService: IEditorService, - @IInstantiationService private readonly instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService, - @ITextFileService private readonly textFileService: ITextFileService + @IDirtyDiffModelService private readonly dirtyDiffModelService: IDirtyDiffModelService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, ) { super(); this.stylesheet = domStylesheetsJs.createStyleSheet(undefined, undefined, this._store); @@ -1624,7 +1153,7 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor this.setViewState({ ...this.viewState, visibility }); } - private setViewState(state: IViewState): void { + private setViewState(state: DirtyDiffWorkbenchControllerViewState): void { this.viewState = state; this.stylesheet.textContent = ` .monaco-editor .dirty-diff-added, @@ -1664,57 +1193,59 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor this.transientDisposables.clear(); - for (const [, dirtyDiff] of this.items) { - dispose(dirtyDiff.values()); + for (const [uri, decoratorMap] of this.decorators.entries()) { + decoratorMap.dispose(); + this.decorators.delete(uri); } - this.items.clear(); this.enabled = false; } private onEditorsChanged(): void { for (const editor of this.editorService.visibleTextEditorControls) { - if (isCodeEditor(editor)) { - const textModel = editor.getModel(); - const controller = DirtyDiffController.get(editor); + if (!isCodeEditor(editor)) { + continue; + } - if (controller) { - controller.modelRegistry = this; - } + const textModel = editor.getModel(); + if (!textModel) { + continue; + } - if (textModel && (!this.items.has(textModel.uri) || !this.items.get(textModel.uri)!.has(editor.getId()))) { - const textFileModel = this.textFileService.files.get(textModel.uri); - - if (textFileModel?.isResolved()) { - const dirtyDiffModel = this.instantiationService.createInstance(DirtyDiffModel, textFileModel, undefined); - const decorator = new DirtyDiffDecorator(textFileModel.textEditorModel, editor, dirtyDiffModel, this.configurationService); - if (!this.items.has(textModel.uri)) { - this.items.set(textModel.uri, new Map()); - } - this.items.get(textModel.uri)?.set(editor.getId(), new DirtyDiffItem(dirtyDiffModel, decorator)); - } - } + const editorId = editor.getId(); + if (this.decorators.get(textModel.uri)?.has(editorId)) { + continue; + } + + const dirtyDiffModel = this.dirtyDiffModelService.getDirtyDiffModel(textModel.uri); + if (!dirtyDiffModel) { + continue; + } + + if (!this.decorators.has(textModel.uri)) { + this.decorators.set(textModel.uri, new DisposableMap()); } + + this.decorators.get(textModel.uri)!.set(editorId, new DirtyDiffDecorator(editor, dirtyDiffModel, this.configurationService)); } - for (const [uri, item] of this.items) { - for (const editorId of item.keys()) { - if (!this.editorService.visibleTextEditorControls.find(editor => isCodeEditor(editor) && editor.getModel()?.uri.toString() === uri.toString() && editor.getId() === editorId)) { - if (item.has(editorId)) { - const dirtyDiffItem = item.get(editorId); - dirtyDiffItem?.dispose(); - item.delete(editorId); - if (item.size === 0) { - this.items.delete(uri); - } - } + // Dispose decorators for editors that are no longer visible + for (const [uri, decoratorMap] of this.decorators.entries()) { + for (const editorId of decoratorMap.keys()) { + const codeEditor = this.editorService.visibleTextEditorControls + .find(editor => isCodeEditor(editor) && editor.getId() === editorId && + this.uriIdentityService.extUri.isEqual(editor.getModel()?.uri, uri)); + + if (!codeEditor) { + decoratorMap.deleteAndDispose(editorId); } } - } - } - getModel(editorModel: ITextModel, codeEditor: ICodeEditor): DirtyDiffModel | undefined { - return this.items.get(editorModel.uri)?.get(codeEditor.getId())?.model; + if (decoratorMap.size === 0) { + decoratorMap.dispose(); + this.decorators.delete(uri); + } + } } override dispose(): void { diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 152b8f2ad0e..d7e997d29ff 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -40,6 +40,7 @@ import { isSCMRepository } from './util.js'; import { SCMHistoryViewPane } from './scmHistoryViewPane.js'; import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js'; import { RemoteNameContext } from '../../../common/contextkeys.js'; +import { DirtyDiffModelService, IDirtyDiffModelService } from './dirtyDiffModel.js'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -596,3 +597,4 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ registerSingleton(ISCMService, SCMService, InstantiationType.Delayed); registerSingleton(ISCMViewService, SCMViewService, InstantiationType.Delayed); registerSingleton(IQuickDiffService, QuickDiffService, InstantiationType.Delayed); +registerSingleton(IDirtyDiffModelService, DirtyDiffModelService, InstantiationType.Delayed); From acc7cf2c73361360267890d99b4818f7cba47131 Mon Sep 17 00:00:00 2001 From: Alen Ajam Date: Mon, 9 Dec 2024 00:27:08 +0900 Subject: [PATCH 035/479] fix: set _lastFocusedWidget as undefined on widget blur (#234610) --- src/vs/platform/list/browser/listService.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index f93947e26bd..4bedb6c7de0 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -90,6 +90,11 @@ export class ListService implements IListService { return combinedDisposable( widget.onDidFocus(() => this.setLastFocusedList(widget)), + widget.onDidBlur(() => { + if (this._lastFocusedWidget === widget) { + this.setLastFocusedList(undefined); + } + }), toDisposable(() => this.lists.splice(this.lists.indexOf(registeredList), 1)), widget.onDidDispose(() => { this.lists = this.lists.filter(l => l !== registeredList); From 6d8bd758ef998677395cffc45a3bea3752ef001c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 8 Dec 2024 07:52:26 -0800 Subject: [PATCH 036/479] Clarify tests by using existing folders (#235209) The old src/ case was removed as it was actually wrong when the folder exists, and that's covered below. --- .../terminal-suggest/src/test/terminalSuggestMain.test.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts index 031c27dafda..c1979b251e2 100644 --- a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts +++ b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts @@ -105,12 +105,11 @@ const testSpecs2: ISuiteSpec[] = [ { input: 'cd ~|', expectedCompletions: ['~'], expectedResourceRequests: { type: 'folders', cwd: testCwd } }, // Relative paths - { input: 'cd s|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, - { input: 'cd src|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, - { input: 'cd src/|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, + { input: 'cd c|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, + { input: 'cd child|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, { input: 'cd .|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, { input: 'cd ./|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, - { input: 'cd ./src|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, + { input: 'cd ./child|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, { input: 'cd ..|', expectedResourceRequests: { type: 'folders', cwd: testCwd } }, // Relative directories (changes cwd due to /) From fc90ee9b9237ee6368fe9fde3a6e23f530e9cd26 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 8 Dec 2024 20:41:43 +0100 Subject: [PATCH 037/479] chat - resolve entitlements after potential upgrade (#235580) --- .../chatContentParts/chatQuotaExceededPart.ts | 8 +- .../contrib/chat/browser/chatQuotasService.ts | 41 ++-------- .../contrib/chat/browser/chatSetup.ts | 75 ++++++++++++++++++- 3 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts index bd584402ca0..e3e06c9b05a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts @@ -11,11 +11,9 @@ import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { assertType } from '../../../../../base/common/types.js'; -import { URI } from '../../../../../base/common/uri.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { localize } from '../../../../../nls.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; -import { IProductService } from '../../../../../platform/product/common/productService.js'; import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js'; import { asCssVariable, textLinkForeground } from '../../../../../platform/theme/common/colorRegistry.js'; import { IChatResponseViewModel } from '../../common/chatViewModel.js'; @@ -34,8 +32,7 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar element: IChatResponseViewModel, renderer: MarkdownRenderer, @IChatWidgetService chatWidgetService: IChatWidgetService, - @ICommandService commandService: ICommandService, - @IProductService productService: IProductService, + @ICommandService commandService: ICommandService ) { super(); @@ -56,8 +53,7 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar let didAddSecondary = false; this._register(button1.onDidClick(async () => { - const url = productService.defaultChatAgent?.upgradePlanUrl; - await commandService.executeCommand('vscode.open', url ? URI.parse(url) : undefined); + await commandService.executeCommand('workbench.action.chat.upgradePlan'); if (!didAddSecondary) { didAddSecondary = true; diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index efa9304c974..467affc7899 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -11,17 +11,16 @@ import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { createDecorator, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; -import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import product from '../../../../platform/product/common/product.js'; -import { URI } from '../../../../base/common/uri.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; export const IChatQuotasService = createDecorator('chatQuotasService'); @@ -102,35 +101,6 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService private registerActions(): void { const that = this; - class UpgradePlanAction extends Action2 { - constructor() { - super({ - id: 'workbench.action.chat.upgradePlan', - title: localize2('managePlan', "Upgrade to Copilot Pro"), - category: localize2('chat.category', 'Chat'), - f1: true, - precondition: ChatContextKeys.enabled, - menu: { - id: MenuId.ChatCommandCenter, - group: 'a_first', - order: 1, - when: ContextKeyExpr.and( - ChatContextKeys.Setup.installed, - ContextKeyExpr.or( - ChatContextKeys.chatQuotaExceeded, - ChatContextKeys.completionsQuotaExceeded - ) - ) - } - }); - } - - override async run(accessor: ServicesAccessor): Promise { - const openerService = accessor.get(IOpenerService); - openerService.open(URI.parse(product.defaultChatAgent?.upgradePlanUrl ?? '')); - } - } - class ShowLimitReachedDialogAction extends Action2 { constructor() { @@ -141,7 +111,7 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService } override async run(accessor: ServicesAccessor) { - const openerService = accessor.get(IOpenerService); + const commandService = accessor.get(ICommandService); const dialogService = accessor.get(IDialogService); const dateFormatter = safeIntl.DateTimeFormat(language, { year: 'numeric', month: 'long', day: 'numeric' }); @@ -168,7 +138,7 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService buttons: [ { label: localize('managePlan', "Upgrade to Copilot Pro"), - run: () => { openerService.open(URI.parse(product.defaultChatAgent?.upgradePlanUrl ?? '')); } + run: () => commandService.executeCommand('workbench.action.chat.upgradePlan') }, ], custom: { @@ -211,7 +181,6 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService } } - registerAction2(UpgradePlanAction); registerAction2(ShowLimitReachedDialogAction); if (product.quality !== 'stable') { registerAction2(SimulateCopilotQuotaExceeded); diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index d31113c0d6b..6d5104baddf 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -7,7 +7,7 @@ import './media/chatViewSetup.css'; import { $, getActiveElement, setVisibility } from '../../../../base/browser/dom.js'; import { Button, ButtonWithDropdown } from '../../../../base/browser/ui/button/button.js'; import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; -import { IAction, toAction } from '../../../../base/common/actions.js'; +import { IAction, toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../base/common/actions.js'; import { Barrier, timeout } from '../../../../base/common/async.js'; import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { Codicon } from '../../../../base/common/codicons.js'; @@ -15,7 +15,7 @@ import { isCancellationError } from '../../../../base/common/errors.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Lazy } from '../../../../base/common/lazy.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { IRequestContext } from '../../../../base/parts/request/common/request.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; @@ -56,6 +56,9 @@ import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID } from './chatView import { ChatViewsWelcomeExtensions, IChatViewsWelcomeContributionRegistry } from './viewsWelcome/chatViewsWelcome.js'; import { IChatQuotasService } from './chatQuotasService.js'; import { mainWindow } from '../../../../base/browser/window.js'; +import { IOpenerService } from '../../../../platform/opener/common/opener.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IHostService } from '../../../services/host/browser/host.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -231,6 +234,61 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr } } + const windowFocusListener = this._register(new MutableDisposable()); + class UpgradePlanAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.chat.upgradePlan', + title: localize2('managePlan', "Upgrade to Copilot Pro"), + category: localize2('chat.category', 'Chat'), + f1: true, + precondition: ChatContextKeys.enabled, + menu: { + id: MenuId.ChatCommandCenter, + group: 'a_first', + order: 1, + when: ContextKeyExpr.and( + ChatContextKeys.Setup.installed, + ContextKeyExpr.or( + ChatContextKeys.chatQuotaExceeded, + ChatContextKeys.completionsQuotaExceeded + ) + ) + } + }); + } + + override async run(accessor: ServicesAccessor): Promise { + const openerService = accessor.get(IOpenerService); + const productService = accessor.get(IProductService); + const telemetryService = accessor.get(ITelemetryService); + const hostService = accessor.get(IHostService); + const commandService = accessor.get(ICommandService); + + telemetryService.publicLog2('workbenchActionExecuted', { id: this.desc.id, from: 'chat' }); + + openerService.open(URI.parse(productService.defaultChatAgent?.upgradePlanUrl ?? '')); + + const entitlement = that.context.state.entitlement; + if (entitlement !== ChatEntitlement.Pro) { + // If the user is not yet Pro, we listen to window focus to refresh the token + // when the user has come back to the window assuming the user signed up. + windowFocusListener.value = hostService.onDidChangeFocus(focus => this.onWindowFocus(focus, commandService)); + } + } + + private async onWindowFocus(focus: boolean, commandService: ICommandService): Promise { + if (focus) { + windowFocusListener.clear(); + + const entitlement = await that.requests.forceResolveEntitlement(undefined); + if (entitlement === ChatEntitlement.Pro) { + commandService.executeCommand('github.copilot.refreshToken'); // ugly, but we need to signal to the extension that entitlements changed + } + } + } + } + async function hideSetupView(viewsDescriptorService: IViewDescriptorService, layoutService: IWorkbenchLayoutService): Promise { const location = viewsDescriptorService.getViewLocationById(ChatViewId); @@ -246,6 +304,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr registerAction2(ChatSetupTriggerAction); registerAction2(ChatSetupHideAction); + registerAction2(UpgradePlanAction); } } @@ -507,7 +566,15 @@ class ChatSetupRequests extends Disposable { } } - async forceResolveEntitlement(session: AuthenticationSession): Promise { + async forceResolveEntitlement(session: AuthenticationSession | undefined): Promise { + if (!session) { + session = await this.findMatchingProviderSession(CancellationToken.None); + } + + if (!session) { + return undefined; + } + return this.resolveEntitlement(session, CancellationToken.None); } @@ -798,7 +865,7 @@ class ChatSetupWelcomeContent extends Disposable { } // Limited SKU - const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}) with 50 chat messages and 2000 code completions per month.", defaultChat.skusDocumentationUrl); + const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}) with 2,000 code completions and 50 chat messages per month.", defaultChat.skusDocumentationUrl); const limitedSkuHeaderContainer = this.element.appendChild($('p')); limitedSkuHeaderContainer.appendChild(this._register(markdown.render(new MarkdownString(limitedSkuHeader, { isTrusted: true, supportThemeIcons: true }))).element); From c61b174d4b33904ab8520430ae7b12749d423ae5 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 9 Dec 2024 06:11:45 +0000 Subject: [PATCH 038/479] Fix missing responses when transferring messages into chat panel (#235583) * Fix missing responses when transferring messages into chat panel Fix #235531 * Add test --- src/vs/workbench/contrib/chat/common/chatModel.ts | 2 +- .../contrib/chat/test/common/chatModel.test.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index ee0c9c98053..14fa03dffa0 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -1152,7 +1152,7 @@ export class ChatModel extends Disposable implements IChatModel { addRequest(message: IParsedChatRequest, variableData: IChatRequestVariableData, attempt: number, chatAgent?: IChatAgentData, slashCommand?: IChatAgentCommand, confirmation?: string, locationData?: IChatLocationData, attachments?: IChatRequestVariableEntry[], workingSet?: URI[], isCompleteAddedRequest?: boolean): ChatRequestModel { const request = new ChatRequestModel(this, message, variableData, Date.now(), attempt, confirmation, locationData, attachments, workingSet, isCompleteAddedRequest); - request.response = new ChatResponseModel([], this, chatAgent, slashCommand, request.id, undefined, undefined, undefined, undefined, undefined, undefined, undefined, isCompleteAddedRequest); + request.response = new ChatResponseModel([], this, chatAgent, slashCommand, request.id, undefined, undefined, undefined, undefined, undefined, undefined, isCompleteAddedRequest); this._requests.push(request); this._lastMessageDate = Date.now(); diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index 1beb12e6ade..be40ba599bd 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -151,6 +151,21 @@ suite('ChatModel', () => { assert.strictEqual(request1.response.response.toString(), 'Hello'); }); + + test('addCompleteRequest', async function () { + const model1 = testDisposables.add(instantiationService.createInstance(ChatModel, undefined, ChatAgentLocation.Panel)); + + model1.startInitialize(); + model1.initialize(undefined); + + const text = 'hello'; + const request1 = model1.addRequest({ text, parts: [new ChatRequestTextPart(new OffsetRange(0, text.length), new Range(1, text.length, 1, text.length), text)] }, { variables: [] }, 0, undefined, undefined, undefined, undefined, undefined, undefined, true); + + assert.strictEqual(request1.isCompleteAddedRequest, true); + assert.strictEqual(request1.response!.isCompleteAddedRequest, true); + assert.strictEqual(request1.isHidden, false); + assert.strictEqual(request1.response!.isHidden, false); + }); }); suite('Response', () => { From f10bb2140999946ed845db28da29f65a75373ae7 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:00:27 +0100 Subject: [PATCH 039/479] SCM - switch to using ReferenceCollection in the IdirtyDiffModelService (#235579) Initial implementation --- .../api/browser/mainThreadEditors.ts | 46 ++++--- .../mainThreadDocumentsAndEditors.test.ts | 4 +- .../contrib/scm/browser/dirtyDiffModel.ts | 120 ++++++------------ .../contrib/scm/browser/dirtydiffDecorator.ts | 102 ++++++++------- 4 files changed, 129 insertions(+), 143 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index deeecec96f2..024a09f283a 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -96,7 +96,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { this._proxy.$acceptEditorPropertiesChanged(id, data); })); - const diffInformationObs = this._getTextEditorDiffInformation(textEditor); + const diffInformationObs = this._getTextEditorDiffInformation(textEditor, toDispose); toDispose.push(autorun(reader => { const diffInformation = diffInformationObs.read(reader); this._proxy.$acceptEditorDiffInformation(id, diffInformation); @@ -131,17 +131,17 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { return result; } - private _getTextEditorDiffInformation(textEditor: MainThreadTextEditor): IObservable { + private _getTextEditorDiffInformation(textEditor: MainThreadTextEditor, toDispose: IDisposable[]): IObservable { const codeEditor = textEditor.getCodeEditor(); if (!codeEditor) { return constObservable(undefined); } // Check if the TextModel belongs to a DiffEditor - const diffEditors = this._codeEditorService.listDiffEditors(); - const [diffEditor] = diffEditors.filter(d => - d.getOriginalEditor().getId() === codeEditor.getId() || - d.getModifiedEditor().getId() === codeEditor.getId()); + const [diffEditor] = this._codeEditorService.listDiffEditors() + .filter(d => + d.getOriginalEditor().getId() === codeEditor.getId() || + d.getModifiedEditor().getId() === codeEditor.getId()); const editorModelObs = diffEditor ? observableFromEvent(this, diffEditor.onDidChangeModel, () => diffEditor.getModel()) @@ -159,13 +159,14 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // TextEditor if (isITextModel(editorModel)) { - const dirtyDiffModel = this._dirtyDiffModelService.getDirtyDiffModel(editorModelUri); - if (!dirtyDiffModel) { + const dirtyDiffModelRef = this._dirtyDiffModelService.createDirtyDiffModelReference(editorModelUri); + if (!dirtyDiffModelRef) { return constObservable(undefined); } - return observableFromEvent(this, dirtyDiffModel.onDidChange, () => { - return dirtyDiffModel.getQuickDiffResults() + toDispose.push(dirtyDiffModelRef); + return observableFromEvent(this, dirtyDiffModelRef.object.onDidChange, () => { + return dirtyDiffModelRef.object.getQuickDiffResults() .map(result => ({ original: result.original, modified: result.modified, @@ -178,13 +179,14 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // we can provide multiple "original resources" to diff with the modified // resource. const diffAlgorithm = this._configurationService.getValue('diffEditor.diffAlgorithm'); - const dirtyDiffModel = this._dirtyDiffModelService.getDiffModel(editorModelUri, diffAlgorithm); - if (!dirtyDiffModel) { + const dirtyDiffModelRef = this._dirtyDiffModelService.createDiffModelReference(editorModelUri, diffAlgorithm); + if (!dirtyDiffModelRef) { return constObservable(undefined); } - return observableFromEvent(Event.any(dirtyDiffModel.onDidChange, diffEditor.onDidUpdateDiff), () => { - const dirtyDiffInformation = dirtyDiffModel.getQuickDiffResults() + toDispose.push(dirtyDiffModelRef); + return observableFromEvent(Event.any(dirtyDiffModelRef.object.onDidChange, diffEditor.onDidUpdateDiff), () => { + const dirtyDiffInformation = dirtyDiffModelRef.object.getQuickDiffResults() .map(result => ({ original: result.original, modified: result.modified, @@ -389,11 +391,19 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { return Promise.resolve([]); } - const dirtyDiffModel = this._dirtyDiffModelService.getDirtyDiffModel(codeEditor.getModel().uri); - const scmQuickDiff = dirtyDiffModel?.quickDiffs.find(quickDiff => quickDiff.isSCM); - const scmQuickDiffChanges = dirtyDiffModel?.changes.filter(change => change.label === scmQuickDiff?.label); + const dirtyDiffModelRef = this._dirtyDiffModelService.createDirtyDiffModelReference(codeEditor.getModel().uri); + if (!dirtyDiffModelRef) { + return Promise.resolve([]); + } - return Promise.resolve(scmQuickDiffChanges?.map(change => change.change) ?? []); + try { + const scmQuickDiff = dirtyDiffModelRef.object.quickDiffs.find(quickDiff => quickDiff.isSCM); + const scmQuickDiffChanges = dirtyDiffModelRef.object.changes.filter(change => change.label === scmQuickDiff?.label); + + return Promise.resolve(scmQuickDiffChanges.map(change => change.change) ?? []); + } finally { + dirtyDiffModelRef.dispose(); + } } } diff --git a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts index 230fc9d2126..015cad1161c 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts @@ -125,10 +125,10 @@ suite('MainThreadDocumentsAndEditors', () => { new TestPathService(), new TestConfigurationService(), new class extends mock() { - override getDirtyDiffModel() { + override createDiffModelReference() { return undefined; } - override getDiffModel() { + override createDirtyDiffModelReference() { return undefined; } } diff --git a/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts b/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts index 3b49650a294..9d9c1d395aa 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts @@ -6,10 +6,7 @@ import { ResourceMap } from '../../../../base/common/map.js'; import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { EncodingMode, IResolvedTextFileEditorModel, isTextFileEditorModel, ITextFileEditorModel, ITextFileService } from '../../../services/textfile/common/textfiles.js'; -import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { autorun, observableFromEvent } from '../../../../base/common/observable.js'; -import { isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; +import { Disposable, DisposableMap, DisposableStore, IReference, ReferenceCollection } from '../../../../base/common/lifecycle.js'; import { DiffAlgorithmName, IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { URI } from '../../../../base/common/uri.js'; @@ -38,108 +35,73 @@ export interface IDirtyDiffModelService { _serviceBrand: undefined; /** - * Returns `undefined` if the editor model is not resolved - * @param uri + * Returns `undefined` if the editor model is not resolved. + * Model refrence has to be disposed once not needed anymore. + * @param resource + * @param algorithm */ - getDirtyDiffModel(uri: URI): DirtyDiffModel | undefined; + createDiffModelReference(resource: URI, algorithm: DiffAlgorithmName): IReference | undefined; /** - * Returns `undefined` if the editor model is not resolved - * @param uri - * @param algorithm + * Returns `undefined` if the editor model is not resolved. + * Model refrence has to be disposed once not needed anymore. + * @param resource */ - getDiffModel(uri: URI, algorithm: DiffAlgorithmName): DirtyDiffModel | undefined; + createDirtyDiffModelReference(resource: URI): IReference | undefined; } -export class DirtyDiffModelService extends Disposable implements IDirtyDiffModelService { - _serviceBrand: undefined; +class DirtyDiffModelReferenceCollection extends ReferenceCollection { + constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { + super(); + } - private readonly _dirtyDiffModels = new ResourceMap(); - private readonly _diffModels = new ResourceMap>(); + protected override createReferencedObject(_key: string, textFileModel: IResolvedTextFileEditorModel, algorithm: DiffAlgorithmName | undefined): DirtyDiffModel { + return this._instantiationService.createInstance(DirtyDiffModel, textFileModel, algorithm); + } - private _visibleTextEditorControls = observableFromEvent( - this.editorService.onDidVisibleEditorsChange, - () => this.editorService.visibleTextEditorControls); + protected override destroyReferencedObject(_key: string, object: DirtyDiffModel): void { + object.dispose(); + } +} + +export class DirtyDiffModelService implements IDirtyDiffModelService { + _serviceBrand: undefined; + + private readonly _references: DirtyDiffModelReferenceCollection; constructor( - @IEditorService private readonly editorService: IEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, @ITextFileService private readonly textFileService: ITextFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { - super(); - - this._register(autorun(reader => { - const visibleTextEditorControls = this._visibleTextEditorControls.read(reader); - - // Dispose dirty diff models for text editors that are not visible - for (const [uri, dirtyDiffModel] of this._dirtyDiffModels) { - const textEditorControl = visibleTextEditorControls - .find(editor => isCodeEditor(editor) && - this.uriIdentityService.extUri.isEqual(editor.getModel()?.uri, uri)); - - if (textEditorControl) { - continue; - } - - dirtyDiffModel.dispose(); - this._dirtyDiffModels.delete(uri); - } - - // Dispose diff models for diff editors that are not visible - for (const [uri, dirtyDiffModel] of this._diffModels) { - const diffEditorControl = visibleTextEditorControls - .find(editor => isDiffEditor(editor) && - this.uriIdentityService.extUri.isEqual(editor.getModel()?.modified.uri, uri)); - - if (diffEditorControl) { - continue; - } - - for (const algorithm of dirtyDiffModel.keys()) { - dirtyDiffModel.get(algorithm)?.dispose(); - dirtyDiffModel.delete(algorithm); - } - this._diffModels.delete(uri); - } - })); + this._references = this.instantiationService.createInstance(DirtyDiffModelReferenceCollection); } - getDirtyDiffModel(uri: URI): DirtyDiffModel | undefined { - let model = this._dirtyDiffModels.get(uri); - if (model) { - return model; - } - - const textFileModel = this.textFileService.files.get(uri); + createDiffModelReference(resource: URI, algorithm: DiffAlgorithmName): IReference | undefined { + const textFileModel = this.textFileService.files.get(resource); if (!textFileModel?.isResolved()) { return undefined; } - model = this.instantiationService.createInstance(DirtyDiffModel, textFileModel, undefined); - this._dirtyDiffModels.set(uri, model); - - return model; + return this._createModelReference(resource, textFileModel, algorithm); } - getDiffModel(uri: URI, algorithm: DiffAlgorithmName): DirtyDiffModel | undefined { - let model = this._diffModels.get(uri)?.get(algorithm); - if (model) { - return model; - } - - const textFileModel = this.textFileService.files.get(uri); + createDirtyDiffModelReference(resource: URI): IReference | undefined { + const textFileModel = this.textFileService.files.get(resource); if (!textFileModel?.isResolved()) { return undefined; } - model = this.instantiationService.createInstance(DirtyDiffModel, textFileModel, algorithm); - if (!this._diffModels.has(uri)) { - this._diffModels.set(uri, new Map()); - } - this._diffModels.get(uri)!.set(algorithm, model); + return this._createModelReference(resource, textFileModel, undefined); + } + + private _createModelReference(resource: URI, textFileModel: IResolvedTextFileEditorModel, algorithm: DiffAlgorithmName | undefined): IReference { + resource = algorithm === undefined + ? this.uriIdentityService.asCanonicalUri(resource) + : this.uriIdentityService.asCanonicalUri(resource) + .with({ query: `algorithm=${algorithm}` }); - return model; + return this._references.acquire(resource.toString(), textFileModel, algorithm); } } diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 473c2f5d418..f14fd38555f 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -6,7 +6,7 @@ import * as nls from '../../../../nls.js'; import './media/dirtydiffDecorator.css'; -import { IDisposable, toDisposable, Disposable, DisposableStore, DisposableMap } from '../../../../base/common/lifecycle.js'; +import { IDisposable, toDisposable, Disposable, DisposableStore, DisposableMap, IReference } from '../../../../base/common/lifecycle.js'; import { Event } from '../../../../base/common/event.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -533,16 +533,20 @@ export class GotoPreviousChangeAction extends EditorAction { return; } - const model = dirtyDiffModelService.getDirtyDiffModel(outerEditor.getModel().uri); - if (!model || model.changes.length === 0) { - return; - } + const modelRef = dirtyDiffModelService.createDirtyDiffModelReference(outerEditor.getModel().uri); + try { + if (!modelRef || modelRef.object.changes.length === 0) { + return; + } - const lineNumber = outerEditor.getPosition().lineNumber; - const index = model.findPreviousClosestChange(lineNumber, false); - const change = model.changes[index]; - await playAccessibilitySymbolForChange(change.change, accessibilitySignalService); - setPositionAndSelection(change.change, outerEditor, accessibilityService, codeEditorService); + const lineNumber = outerEditor.getPosition().lineNumber; + const index = modelRef.object.findPreviousClosestChange(lineNumber, false); + const change = modelRef.object.changes[index]; + await playAccessibilitySymbolForChange(change.change, accessibilitySignalService); + setPositionAndSelection(change.change, outerEditor, accessibilityService, codeEditorService); + } finally { + modelRef?.dispose(); + } } } registerEditorAction(GotoPreviousChangeAction); @@ -569,17 +573,20 @@ export class GotoNextChangeAction extends EditorAction { return; } - const model = dirtyDiffModelService.getDirtyDiffModel(outerEditor.getModel().uri); + const modelRef = dirtyDiffModelService.createDirtyDiffModelReference(outerEditor.getModel().uri); + try { + if (!modelRef || modelRef.object.changes.length === 0) { + return; + } - if (!model || model.changes.length === 0) { - return; + const lineNumber = outerEditor.getPosition().lineNumber; + const index = modelRef.object.findNextClosestChange(lineNumber, false); + const change = modelRef.object.changes[index].change; + await playAccessibilitySymbolForChange(change, accessibilitySignalService); + setPositionAndSelection(change, outerEditor, accessibilityService, codeEditorService); + } finally { + modelRef?.dispose(); } - - const lineNumber = outerEditor.getPosition().lineNumber; - const index = model.findNextClosestChange(lineNumber, false); - const change = model.changes[index].change; - await playAccessibilitySymbolForChange(change, accessibilitySignalService); - setPositionAndSelection(change, outerEditor, accessibilityService, codeEditorService); } } @@ -774,29 +781,31 @@ export class DirtyDiffController extends Disposable implements IEditorContributi return false; } - const model = this.dirtyDiffModelService.getDirtyDiffModel(editorModel.uri); + const modelRef = this.dirtyDiffModelService.createDirtyDiffModelReference(editorModel.uri); - if (!model) { + if (!modelRef) { return false; } - if (model.changes.length === 0) { + if (modelRef.object.changes.length === 0) { + modelRef.dispose(); return false; } - this.model = model; - this.widget = this.instantiationService.createInstance(DirtyDiffWidget, this.editor, model); + this.model = modelRef.object; + this.widget = this.instantiationService.createInstance(DirtyDiffWidget, this.editor, this.model); this.isDirtyDiffVisible.set(true); const disposables = new DisposableStore(); disposables.add(Event.once(this.widget.onDidClose)(this.close, this)); - const onDidModelChange = Event.chain(model.onDidChange, $ => + const onDidModelChange = Event.chain(this.model.onDidChange, $ => $.filter(e => e.diff.length > 0) .map(e => e.diff) ); onDidModelChange(this.onDidModelChange, this, disposables); + disposables.add(modelRef); disposables.add(this.widget); disposables.add(toDisposable(() => { this.model = null; @@ -883,22 +892,27 @@ export class DirtyDiffController extends Disposable implements IEditorContributi return; } - const model = this.dirtyDiffModelService.getDirtyDiffModel(editorModel.uri); + const modelRef = this.dirtyDiffModelService.createDirtyDiffModelReference(editorModel.uri); - if (!model) { + if (!modelRef) { return; } - const index = model.changes.findIndex(change => lineIntersectsChange(lineNumber, change.change)); + try { + const index = modelRef.object.changes + .findIndex(change => lineIntersectsChange(lineNumber, change.change)); - if (index < 0) { - return; - } + if (index < 0) { + return; + } - if (index === this.widget?.index) { - this.close(); - } else { - this.next(lineNumber); + if (index === this.widget?.index) { + this.close(); + } else { + this.next(lineNumber); + } + } finally { + modelRef.dispose(); } } @@ -973,7 +987,7 @@ class DirtyDiffDecorator extends Disposable { constructor( private readonly codeEditor: ICodeEditor, - private readonly dirtyDiffModel: DirtyDiffModel, + private readonly dirtyDiffModelRef: IReference, @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); @@ -1022,7 +1036,7 @@ class DirtyDiffDecorator extends Disposable { } })); - this._register(Event.runAndSubscribe(dirtyDiffModel.onDidChange, () => this.onDidChange())); + this._register(Event.runAndSubscribe(this.dirtyDiffModelRef.object.onDidChange, () => this.onDidChange())); } private onDidChange(): void { @@ -1030,10 +1044,10 @@ class DirtyDiffDecorator extends Disposable { return; } - const visibleQuickDiffs = this.dirtyDiffModel.quickDiffs.filter(quickDiff => quickDiff.visible); + const visibleQuickDiffs = this.dirtyDiffModelRef.object.quickDiffs.filter(quickDiff => quickDiff.visible); const pattern = this.configurationService.getValue<{ added: boolean; modified: boolean }>('scm.diffDecorationsGutterPattern'); - const decorations = this.dirtyDiffModel.changes + const decorations = this.dirtyDiffModelRef.object.changes .filter(labeledChange => visibleQuickDiffs.some(quickDiff => quickDiff.label === labeledChange.label)) .map((labeledChange) => { const change = labeledChange.change; @@ -1080,8 +1094,8 @@ class DirtyDiffDecorator extends Disposable { if (this.decorationsCollection) { this.decorationsCollection?.clear(); } - this.decorationsCollection = undefined; + this.dirtyDiffModelRef.dispose(); super.dispose(); } } @@ -1217,8 +1231,8 @@ export class DirtyDiffWorkbenchController extends Disposable implements IWorkben continue; } - const dirtyDiffModel = this.dirtyDiffModelService.getDirtyDiffModel(textModel.uri); - if (!dirtyDiffModel) { + const dirtyDiffModelRef = this.dirtyDiffModelService.createDirtyDiffModelReference(textModel.uri); + if (!dirtyDiffModelRef) { continue; } @@ -1226,10 +1240,10 @@ export class DirtyDiffWorkbenchController extends Disposable implements IWorkben this.decorators.set(textModel.uri, new DisposableMap()); } - this.decorators.get(textModel.uri)!.set(editorId, new DirtyDiffDecorator(editor, dirtyDiffModel, this.configurationService)); + this.decorators.get(textModel.uri)!.set(editorId, new DirtyDiffDecorator(editor, dirtyDiffModelRef, this.configurationService)); } - // Dispose decorators for editors that are no longer visible + // Dispose decorators for editors that are no longer visible. for (const [uri, decoratorMap] of this.decorators.entries()) { for (const editorId of decoratorMap.keys()) { const codeEditor = this.editorService.visibleTextEditorControls From 291a57a4c7e00fa33a39257b24bf1002a6ea70f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Dec 2024 10:16:36 +0100 Subject: [PATCH 040/479] add docs/desc for atEnd context key (#235595) https://github.com/microsoft/vscode/issues/205415 --- src/vs/editor/contrib/suggest/browser/wordContextKey.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/browser/wordContextKey.ts b/src/vs/editor/contrib/suggest/browser/wordContextKey.ts index 901b440d8a5..65820c8dda4 100644 --- a/src/vs/editor/contrib/suggest/browser/wordContextKey.ts +++ b/src/vs/editor/contrib/suggest/browser/wordContextKey.ts @@ -7,10 +7,11 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { localize } from '../../../../nls.js'; export class WordContextKey { - static readonly AtEnd = new RawContextKey('atEndOfWord', false); + static readonly AtEnd = new RawContextKey('atEndOfWord', false, { type: 'boolean', description: localize('desc', "A context key that is true when at the end of a word. Note that this is only defined when tab-completions are enabled") }); private readonly _ckAtEnd: IContextKey; private readonly _configListener: IDisposable; From 18bd4c58c7566a8bd68c3891ec1870cb653e867c Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 9 Dec 2024 10:26:42 +0100 Subject: [PATCH 041/479] Add more detail to expandable-hover setting description (#235596) * adding more detail to hover * polish --- extensions/typescript-language-features/package.nls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 1e5ca174b55..6a7dc26a24d 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -225,7 +225,7 @@ "configuration.tsserver.web.typeAcquisition.enabled": "Enable/disable package acquisition on the web. This enables IntelliSense for imported packages. Requires `#typescript.tsserver.web.projectWideIntellisense.enabled#`. Currently not supported for Safari.", "configuration.tsserver.nodePath": "Run TS Server on a custom Node installation. This can be a path to a Node executable, or 'node' if you want VS Code to detect a Node installation.", "configuration.updateImportsOnPaste": "Enable updating imports when pasting code. Requires TypeScript 5.7+.\n\nBy default this shows a option to update imports after pasting. You can use the `#editor.pasteAs.preferences#` setting to update imports automatically when pasting: `\"editor.pasteAs.preferences\": [ \"text.updateImports.jsts\" ]`.", - "configuration.expandableHover": "Enable/disable expanding on hover.", + "configuration.expandableHover": "Enable expanding/contracting the hover to reveal more/less information from the TS server.", "walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js", "walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.", "walkthroughs.nodejsWelcome.downloadNode.forMacOrWindows.title": "Install Node.js", From 36cd80a3a3f9a2b5af47d50d8ac0edc4fd8cb502 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 9 Dec 2024 10:49:52 +0100 Subject: [PATCH 042/479] editor sticky scroll: only collapse outermost region with folding icon (#235597) only collapse the given region --- .../contrib/stickyScroll/browser/stickyScrollController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 3387930fcd2..4f94c670552 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -394,7 +394,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib if (!foldingIcon) { return; } - toggleCollapseState(this._foldingModel, Number.MAX_VALUE, [line]); + toggleCollapseState(this._foldingModel, 1, [line]); foldingIcon.isCollapsed = !foldingIcon.isCollapsed; const scrollTop = (foldingIcon.isCollapsed ? this._editor.getTopForLineNumber(foldingIcon.foldingEndLine) From 5410686643f746d444c3379690113a84351ec9e5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Dec 2024 11:09:09 +0100 Subject: [PATCH 043/479] know and honour `TextEditorSelectionSource` (#235599) fixes https://github.com/microsoft/vscode/issues/230928 --- src/vs/workbench/api/common/extHostTypes.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 6cdb305f07c..90f82a69ce0 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -22,6 +22,7 @@ import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from '../. import { RemoteAuthorityResolverErrorCode } from '../../../platform/remote/common/remoteAuthorityResolver.js'; import { CellEditType, ICellMetadataEdit, IDocumentMetadataEdit, isTextStreamMime } from '../../contrib/notebook/common/notebookCommon.js'; import { IRelativePatternDto } from './extHost.protocol.js'; +import { TextEditorSelectionSource } from '../../../platform/editor/common/editor.js'; /** * @deprecated @@ -1943,11 +1944,15 @@ export enum DecorationRangeBehavior { } export namespace TextEditorSelectionChangeKind { - export function fromValue(s: string | undefined) { + export function fromValue(s: TextEditorSelectionSource | string | undefined) { switch (s) { case 'keyboard': return TextEditorSelectionChangeKind.Keyboard; case 'mouse': return TextEditorSelectionChangeKind.Mouse; - case 'api': return TextEditorSelectionChangeKind.Command; + case 'api': + case TextEditorSelectionSource.PROGRAMMATIC: + case TextEditorSelectionSource.JUMP: + case TextEditorSelectionSource.NAVIGATION: + return TextEditorSelectionChangeKind.Command; } return undefined; } From 5ae456f7f1149a933742968ec7c00904f0e5a069 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Dec 2024 11:31:45 +0100 Subject: [PATCH 044/479] chat - setup tweaks (#235600) --- .../contrib/chat/browser/chatSetup.ts | 39 +++++++------------ .../chat/browser/media/chatViewSetup.css | 8 ++-- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 6d5104baddf..485d1489dfd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -23,7 +23,7 @@ import { localize, localize2 } from '../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; @@ -67,6 +67,7 @@ const defaultChat = { termsStatementUrl: product.defaultChatAgent?.termsStatementUrl ?? '', privacyStatementUrl: product.defaultChatAgent?.privacyStatementUrl ?? '', skusDocumentationUrl: product.defaultChatAgent?.skusDocumentationUrl ?? '', + upgradePlanUrl: product.defaultChatAgent?.upgradePlanUrl ?? '', providerId: product.defaultChatAgent?.providerId ?? '', providerName: product.defaultChatAgent?.providerName ?? '', providerScopes: product.defaultChatAgent?.providerScopes ?? [[]], @@ -260,14 +261,13 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr override async run(accessor: ServicesAccessor): Promise { const openerService = accessor.get(IOpenerService); - const productService = accessor.get(IProductService); const telemetryService = accessor.get(ITelemetryService); const hostService = accessor.get(IHostService); const commandService = accessor.get(ICommandService); telemetryService.publicLog2('workbenchActionExecuted', { id: this.desc.id, from: 'chat' }); - openerService.open(URI.parse(productService.defaultChatAgent?.upgradePlanUrl ?? '')); + openerService.open(URI.parse(defaultChat.upgradePlanUrl)); const entitlement = that.context.state.entitlement; if (entitlement !== ChatEntitlement.Pro) { @@ -869,12 +869,6 @@ class ChatSetupWelcomeContent extends Disposable { const limitedSkuHeaderContainer = this.element.appendChild($('p')); limitedSkuHeaderContainer.appendChild(this._register(markdown.render(new MarkdownString(limitedSkuHeader, { isTrusted: true, supportThemeIcons: true }))).element); - // Terms - const terms = localize({ key: 'termsLabel', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to our [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); - const termsContainer = this.element.appendChild($('p')); - termsContainer.classList.add('terms-container'); - termsContainer.appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element); - // Setup Button const actions: IAction[] = []; if (this.context.state.installed) { @@ -882,6 +876,7 @@ class ChatSetupWelcomeContent extends Disposable { actions.push(toAction({ id: 'chatSetup.signInGhe', label: localize('signInGhe', "Sign in with a GHE.com Account"), run: () => this.commandService.executeCommand('github.copilotChat.signInGHE') })); } const buttonContainer = this.element.appendChild($('p')); + buttonContainer.classList.add('button-container'); const button = this._register(actions.length === 0 ? new Button(buttonContainer, { supportIcons: true, ...defaultButtonStyles @@ -894,6 +889,10 @@ class ChatSetupWelcomeContent extends Disposable { })); this._register(button.onDidClick(() => this.controller.setup())); + // Terms + const terms = localize({ key: 'termsLabel', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); + this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element); + // Update based on model state this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button))); } @@ -1056,23 +1055,13 @@ class ChatSetupContext extends Disposable { this.storageService.remove('interactive.sessions', this.workspaceContextService.getWorkspace().folders.length ? StorageScope.WORKSPACE : StorageScope.APPLICATION); } - let changed = false; - changed = this.updateContextKey(this.signedOutContextKey, this._state.entitlement === ChatEntitlement.Unknown) || changed; - changed = this.updateContextKey(this.canSignUpContextKey, this._state.entitlement === ChatEntitlement.Available) || changed; - changed = this.updateContextKey(this.limitedContextKey, this._state.entitlement === ChatEntitlement.Limited) || changed; - changed = this.updateContextKey(this.triggeredContext, !!this._state.triggered) || changed; - changed = this.updateContextKey(this.installedContext, !!this._state.installed) || changed; + this.signedOutContextKey.set(this._state.entitlement === ChatEntitlement.Unknown); + this.canSignUpContextKey.set(this._state.entitlement === ChatEntitlement.Available); + this.limitedContextKey.set(this._state.entitlement === ChatEntitlement.Limited); + this.triggeredContext.set(!!this._state.triggered); + this.installedContext.set(!!this._state.installed); - if (changed) { - this._onDidChange.fire(); - } - } - - private updateContextKey(contextKey: IContextKey, value: boolean): boolean { - const current = contextKey.get(); - contextKey.set(value); - - return current !== value; + this._onDidChange.fire(); } suspend(): void { diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css index 97a06eac5be..8b3a81f428e 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css @@ -17,10 +17,6 @@ background-color: var(--vscode-chat-requestBackground); } - .terms-container { - padding-top: 5px; - } - .chat-feature-container { display: flex; align-items: center; @@ -38,6 +34,10 @@ vertical-align: bottom; } + .button-container { + padding-top: 20px; + } + /** Dropdown Button */ .monaco-button-dropdown { width: 100%; From 0e9de87e69958bd3fd59a68374fb8b65bb04038c Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:35:20 +0100 Subject: [PATCH 045/479] SCM - rename dirtyDiff to quickDiff for consistency (#235601) * Rename dirtyDiff -> quickDiff for consistency * More renames * Extract quick diff widget into separate file --- .../browser/mainThreadDocumentsAndEditors.ts | 6 +- .../api/browser/mainThreadEditors.ts | 36 +- .../mainThreadDocumentsAndEditors.test.ts | 9 +- .../chat/browser/chatEditorController.ts | 2 +- .../contrib/format/browser/formatModified.ts | 3 +- .../chatEdit/notebookCellDecorators.ts | 2 +- .../contrib/scm/browser/dirtyDiffSwitcher.ts | 72 -- .../contrib/scm/browser/quickDiffDecorator.ts | 338 +++++++ .../{dirtyDiffModel.ts => quickDiffModel.ts} | 131 +-- ...rtydiffDecorator.ts => quickDiffWidget.ts} | 888 ++++++------------ .../contrib/scm/browser/scm.contribution.ts | 13 +- .../workbench/contrib/scm/common/quickDiff.ts | 108 +++ .../contrib/scm/common/quickDiffService.ts | 5 + 13 files changed, 801 insertions(+), 812 deletions(-) delete mode 100644 src/vs/workbench/contrib/scm/browser/dirtyDiffSwitcher.ts create mode 100644 src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts rename src/vs/workbench/contrib/scm/browser/{dirtyDiffModel.ts => quickDiffModel.ts} (79%) rename src/vs/workbench/contrib/scm/browser/{dirtydiffDecorator.ts => quickDiffWidget.ts} (58%) diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index 992229b91b7..66ae21fb2f8 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -32,7 +32,7 @@ import { diffSets, diffMaps } from '../../../base/common/collections.js'; import { IPaneCompositePartService } from '../../services/panecomposite/browser/panecomposite.js'; import { ViewContainerLocation } from '../../common/views.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; -import { IDirtyDiffModelService } from '../../contrib/scm/browser/dirtyDiffModel.js'; +import { IQuickDiffModelService } from '../../contrib/scm/browser/quickDiffModel.js'; class TextEditorSnapshot { @@ -298,14 +298,14 @@ export class MainThreadDocumentsAndEditors { @IClipboardService private readonly _clipboardService: IClipboardService, @IPathService pathService: IPathService, @IConfigurationService configurationService: IConfigurationService, - @IDirtyDiffModelService dirtyDiffModelService: IDirtyDiffModelService + @IQuickDiffModelService quickDiffModelService: IQuickDiffModelService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors); this._mainThreadDocuments = this._toDispose.add(new MainThreadDocuments(extHostContext, this._modelService, this._textFileService, fileService, textModelResolverService, environmentService, uriIdentityService, workingCopyFileService, pathService)); extHostContext.set(MainContext.MainThreadDocuments, this._mainThreadDocuments); - this._mainThreadEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, this._editorService, this._editorGroupService, configurationService, dirtyDiffModelService, uriIdentityService)); + this._mainThreadEditors = this._toDispose.add(new MainThreadTextEditors(this, extHostContext, codeEditorService, this._editorService, this._editorGroupService, configurationService, quickDiffModelService, uriIdentityService)); extHostContext.set(MainContext.MainThreadTextEditors, this._mainThreadEditors); // It is expected that the ctor of the state computer calls our `_onDelta`. diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 024a09f283a..a61ba3c7801 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -28,7 +28,7 @@ import { IExtHostContext } from '../../services/extensions/common/extHostCustome import { IEditorControl } from '../../common/editor.js'; import { getCodeEditor, ICodeEditor } from '../../../editor/browser/editorBrowser.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; -import { IDirtyDiffModelService } from '../../contrib/scm/browser/dirtyDiffModel.js'; +import { IQuickDiffModelService } from '../../contrib/scm/browser/quickDiffModel.js'; import { autorun, constObservable, derived, derivedOpts, IObservable, observableFromEvent } from '../../../base/common/observable.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { isITextModel } from '../../../editor/common/model.js'; @@ -61,7 +61,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { @IEditorService private readonly _editorService: IEditorService, @IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IDirtyDiffModelService private readonly _dirtyDiffModelService: IDirtyDiffModelService, + @IQuickDiffModelService private readonly _quickDiffModelService: IQuickDiffModelService, @IUriIdentityService private readonly _uriIdentityService: IUriIdentityService ) { this._instanceId = String(++MainThreadTextEditors.INSTANCE_COUNT); @@ -159,14 +159,14 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // TextEditor if (isITextModel(editorModel)) { - const dirtyDiffModelRef = this._dirtyDiffModelService.createDirtyDiffModelReference(editorModelUri); - if (!dirtyDiffModelRef) { + const quickDiffModelRef = this._quickDiffModelService.createQuickDiffModelReference(editorModelUri); + if (!quickDiffModelRef) { return constObservable(undefined); } - toDispose.push(dirtyDiffModelRef); - return observableFromEvent(this, dirtyDiffModelRef.object.onDidChange, () => { - return dirtyDiffModelRef.object.getQuickDiffResults() + toDispose.push(quickDiffModelRef); + return observableFromEvent(this, quickDiffModelRef.object.onDidChange, () => { + return quickDiffModelRef.object.getQuickDiffResults() .map(result => ({ original: result.original, modified: result.modified, @@ -179,14 +179,14 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { // we can provide multiple "original resources" to diff with the modified // resource. const diffAlgorithm = this._configurationService.getValue('diffEditor.diffAlgorithm'); - const dirtyDiffModelRef = this._dirtyDiffModelService.createDiffModelReference(editorModelUri, diffAlgorithm); - if (!dirtyDiffModelRef) { + const quickDiffModelRef = this._quickDiffModelService.createQuickDiffModelReference(editorModelUri, { algorithm: diffAlgorithm }); + if (!quickDiffModelRef) { return constObservable(undefined); } - toDispose.push(dirtyDiffModelRef); - return observableFromEvent(Event.any(dirtyDiffModelRef.object.onDidChange, diffEditor.onDidUpdateDiff), () => { - const dirtyDiffInformation = dirtyDiffModelRef.object.getQuickDiffResults() + toDispose.push(quickDiffModelRef); + return observableFromEvent(Event.any(quickDiffModelRef.object.onDidChange, diffEditor.onDidUpdateDiff), () => { + const quickDiffInformation = quickDiffModelRef.object.getQuickDiffResults() .map(result => ({ original: result.original, modified: result.modified, @@ -200,7 +200,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { changes: diffChanges.map(change => change as LineRangeMapping) }]; - return [...dirtyDiffInformation, ...diffInformation]; + return [...quickDiffInformation, ...diffInformation]; }); }); @@ -391,18 +391,18 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { return Promise.resolve([]); } - const dirtyDiffModelRef = this._dirtyDiffModelService.createDirtyDiffModelReference(codeEditor.getModel().uri); - if (!dirtyDiffModelRef) { + const quickDiffModelRef = this._quickDiffModelService.createQuickDiffModelReference(codeEditor.getModel().uri); + if (!quickDiffModelRef) { return Promise.resolve([]); } try { - const scmQuickDiff = dirtyDiffModelRef.object.quickDiffs.find(quickDiff => quickDiff.isSCM); - const scmQuickDiffChanges = dirtyDiffModelRef.object.changes.filter(change => change.label === scmQuickDiff?.label); + const scmQuickDiff = quickDiffModelRef.object.quickDiffs.find(quickDiff => quickDiff.isSCM); + const scmQuickDiffChanges = quickDiffModelRef.object.changes.filter(change => change.label === scmQuickDiff?.label); return Promise.resolve(scmQuickDiffChanges.map(change => change.change) ?? []); } finally { - dirtyDiffModelRef.dispose(); + quickDiffModelRef.dispose(); } } } diff --git a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts index 015cad1161c..e077dfc7ac7 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts @@ -36,7 +36,7 @@ import { LanguageService } from '../../../../editor/common/services/languageServ import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; import { TestLanguageConfigurationService } from '../../../../editor/test/common/modes/testLanguageConfigurationService.js'; import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; -import { IDirtyDiffModelService } from '../../../contrib/scm/browser/dirtyDiffModel.js'; +import { IQuickDiffModelService } from '../../../contrib/scm/browser/quickDiffModel.js'; import { ITextEditorDiffInformation } from '../../../../platform/editor/common/editor.js'; suite('MainThreadDocumentsAndEditors', () => { @@ -124,11 +124,8 @@ suite('MainThreadDocumentsAndEditors', () => { }, new TestPathService(), new TestConfigurationService(), - new class extends mock() { - override createDiffModelReference() { - return undefined; - } - override createDirtyDiffModelReference() { + new class extends mock() { + override createQuickDiffModelReference() { return undefined; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts index c4a60c82cc8..0cca055ece2 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts @@ -21,7 +21,6 @@ import { ModelDecorationOptions } from '../../../../editor/common/model/textMode import { InlineDecoration, InlineDecorationType } from '../../../../editor/common/viewModel.js'; import { localize } from '../../../../nls.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../../scm/browser/dirtydiffDecorator.js'; import { ChatEditingSessionState, IChatEditingService, IModifiedFileEntry, WorkingSetEntryState } from '../common/chatEditingService.js'; import { Event } from '../../../../base/common/event.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -31,6 +30,7 @@ import { Position } from '../../../../editor/common/core/position.js'; import { Selection } from '../../../../editor/common/core/selection.js'; import { HiddenItemStrategy, MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; +import { minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../../scm/common/quickDiff.js'; export const ctxHasEditorModification = new RawContextKey('chat.hasEditorModifications', undefined, localize('chat.hasEditorModifications', "The current editor contains chat modifications")); export const ctxHasRequestInProgress = new RawContextKey('chat.ctxHasRequestInProgress', false, localize('chat.ctxHasRequestInProgress', "The current editor shows a file from an edit session which is still in progress")); diff --git a/src/vs/workbench/contrib/format/browser/formatModified.ts b/src/vs/workbench/contrib/format/browser/formatModified.ts index bddd514e821..d0fce02f1c4 100644 --- a/src/vs/workbench/contrib/format/browser/formatModified.ts +++ b/src/vs/workbench/contrib/format/browser/formatModified.ts @@ -17,8 +17,8 @@ import * as nls from '../../../../nls.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { Progress } from '../../../../platform/progress/common/progress.js'; -import { getOriginalResource } from '../../scm/browser/dirtydiffDecorator.js'; import { IQuickDiffService } from '../../scm/common/quickDiff.js'; +import { getOriginalResource } from '../../scm/common/quickDiffService.js'; registerEditorAction(class FormatModifiedAction extends EditorAction { @@ -48,7 +48,6 @@ registerEditorAction(class FormatModifiedAction extends EditorAction { } }); - export async function getModifiedRanges(accessor: ServicesAccessor, modified: ITextModel): Promise { const quickDiffService = accessor.get(IQuickDiffService); const workerService = accessor.get(IEditorWorkerService); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts index 17517c36bff..81a5b3776ac 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts @@ -21,7 +21,6 @@ import { IDocumentDiff } from '../../../../../../editor/common/diff/documentDiff import { ITextModel, TrackedRangeStickiness, MinimapPosition, IModelDeltaDecoration, OverviewRulerLane } from '../../../../../../editor/common/model.js'; import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../../editor/common/viewModel.js'; -import { overviewRulerModifiedForeground, minimapGutterModifiedBackground, overviewRulerAddedForeground, minimapGutterAddedBackground, overviewRulerDeletedForeground, minimapGutterDeletedBackground } from '../../../../scm/browser/dirtydiffDecorator.js'; import { Range } from '../../../../../../editor/common/core/range.js'; import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; import { tokenizeToString } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; @@ -32,6 +31,7 @@ import { DefaultLineHeight } from '../../diff/diffElementViewModel.js'; import { INotebookOriginalCellModelFactory } from './notebookOriginalCellModelFactory.js'; import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; import { isEqual } from '../../../../../../base/common/resources.js'; +import { minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../../../../scm/common/quickDiff.js'; export class NotebookCellDiffDecorator extends DisposableStore { diff --git a/src/vs/workbench/contrib/scm/browser/dirtyDiffSwitcher.ts b/src/vs/workbench/contrib/scm/browser/dirtyDiffSwitcher.ts deleted file mode 100644 index cf2d4469049..00000000000 --- a/src/vs/workbench/contrib/scm/browser/dirtyDiffSwitcher.ts +++ /dev/null @@ -1,72 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from '../../../../nls.js'; -import { Action, IAction } from '../../../../base/common/actions.js'; -import { IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; -import { ISelectOptionItem } from '../../../../base/browser/ui/selectBox/selectBox.js'; -import { SelectActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; -import { defaultSelectBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { peekViewTitleBackground } from '../../../../editor/contrib/peekView/browser/peekView.js'; -import { editorBackground } from '../../../../platform/theme/common/colorRegistry.js'; - -export interface IQuickDiffSelectItem extends ISelectOptionItem { - provider: string; -} - -export class SwitchQuickDiffViewItem extends SelectActionViewItem { - private readonly optionsItems: IQuickDiffSelectItem[]; - - constructor( - action: IAction, - providers: string[], - selected: string, - @IContextViewService contextViewService: IContextViewService, - @IThemeService themeService: IThemeService - ) { - const items = providers.map(provider => ({ provider, text: provider })); - let startingSelection = providers.indexOf(selected); - if (startingSelection === -1) { - startingSelection = 0; - } - const styles = { ...defaultSelectBoxStyles }; - const theme = themeService.getColorTheme(); - const editorBackgroundColor = theme.getColor(editorBackground); - const peekTitleColor = theme.getColor(peekViewTitleBackground); - const opaqueTitleColor = peekTitleColor?.makeOpaque(editorBackgroundColor!) ?? editorBackgroundColor!; - styles.selectBackground = opaqueTitleColor.lighten(.6).toString(); - super(null, action, items, startingSelection, contextViewService, styles, { ariaLabel: nls.localize('remotes', 'Switch quick diff base') }); - this.optionsItems = items; - } - - public setSelection(provider: string) { - const index = this.optionsItems.findIndex(item => item.provider === provider); - this.select(index); - } - - protected override getActionContext(_: string, index: number): IQuickDiffSelectItem { - return this.optionsItems[index]; - } - - override render(container: HTMLElement): void { - super.render(container); - this.setFocusable(true); - } -} - -export class SwitchQuickDiffBaseAction extends Action { - - public static readonly ID = 'quickDiff.base.switch'; - public static readonly LABEL = nls.localize('quickDiff.base.switch', "Switch Quick Diff Base"); - - constructor(private readonly callback: (event?: IQuickDiffSelectItem) => void) { - super(SwitchQuickDiffBaseAction.ID, SwitchQuickDiffBaseAction.LABEL, undefined, undefined); - } - - override async run(event?: IQuickDiffSelectItem): Promise { - return this.callback(event); - } -} diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts new file mode 100644 index 00000000000..8e701d3d92c --- /dev/null +++ b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts @@ -0,0 +1,338 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from '../../../../nls.js'; + +import './media/dirtydiffDecorator.css'; +import { Disposable, DisposableStore, DisposableMap, IReference } from '../../../../base/common/lifecycle.js'; +import { Event } from '../../../../base/common/event.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; +import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; +import { ICodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; +import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; +import { OverviewRulerLane, IModelDecorationOptions, MinimapPosition } from '../../../../editor/common/model.js'; +import * as domStylesheetsJs from '../../../../base/browser/domStylesheets.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { ChangeType, getChangeType, minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../common/quickDiff.js'; +import { QuickDiffModel, IQuickDiffModelService } from './quickDiffModel.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { ResourceMap } from '../../../../base/common/map.js'; +import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; + +class QuickDiffDecorator extends Disposable { + + static createDecoration(className: string, tooltip: string | null, options: { gutter: boolean; overview: { active: boolean; color: string }; minimap: { active: boolean; color: string }; isWholeLine: boolean }): ModelDecorationOptions { + const decorationOptions: IModelDecorationOptions = { + description: 'dirty-diff-decoration', + isWholeLine: options.isWholeLine, + }; + + if (options.gutter) { + decorationOptions.linesDecorationsClassName = `dirty-diff-glyph ${className}`; + decorationOptions.linesDecorationsTooltip = tooltip; + } + + if (options.overview.active) { + decorationOptions.overviewRuler = { + color: themeColorFromId(options.overview.color), + position: OverviewRulerLane.Left + }; + } + + if (options.minimap.active) { + decorationOptions.minimap = { + color: themeColorFromId(options.minimap.color), + position: MinimapPosition.Gutter + }; + } + + return ModelDecorationOptions.createDynamic(decorationOptions); + } + + private addedOptions: ModelDecorationOptions; + private addedPatternOptions: ModelDecorationOptions; + private modifiedOptions: ModelDecorationOptions; + private modifiedPatternOptions: ModelDecorationOptions; + private deletedOptions: ModelDecorationOptions; + private decorationsCollection: IEditorDecorationsCollection | undefined; + + constructor( + private readonly codeEditor: ICodeEditor, + private readonly quickDiffModelRef: IReference, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { + super(); + + const decorations = configurationService.getValue('scm.diffDecorations'); + const gutter = decorations === 'all' || decorations === 'gutter'; + const overview = decorations === 'all' || decorations === 'overview'; + const minimap = decorations === 'all' || decorations === 'minimap'; + + const diffAdded = nls.localize('diffAdded', 'Added lines'); + this.addedOptions = QuickDiffDecorator.createDecoration('dirty-diff-added', diffAdded, { + gutter, + overview: { active: overview, color: overviewRulerAddedForeground }, + minimap: { active: minimap, color: minimapGutterAddedBackground }, + isWholeLine: true + }); + this.addedPatternOptions = QuickDiffDecorator.createDecoration('dirty-diff-added-pattern', diffAdded, { + gutter, + overview: { active: overview, color: overviewRulerAddedForeground }, + minimap: { active: minimap, color: minimapGutterAddedBackground }, + isWholeLine: true + }); + const diffModified = nls.localize('diffModified', 'Changed lines'); + this.modifiedOptions = QuickDiffDecorator.createDecoration('dirty-diff-modified', diffModified, { + gutter, + overview: { active: overview, color: overviewRulerModifiedForeground }, + minimap: { active: minimap, color: minimapGutterModifiedBackground }, + isWholeLine: true + }); + this.modifiedPatternOptions = QuickDiffDecorator.createDecoration('dirty-diff-modified-pattern', diffModified, { + gutter, + overview: { active: overview, color: overviewRulerModifiedForeground }, + minimap: { active: minimap, color: minimapGutterModifiedBackground }, + isWholeLine: true + }); + this.deletedOptions = QuickDiffDecorator.createDecoration('dirty-diff-deleted', nls.localize('diffDeleted', 'Removed lines'), { + gutter, + overview: { active: overview, color: overviewRulerDeletedForeground }, + minimap: { active: minimap, color: minimapGutterDeletedBackground }, + isWholeLine: false + }); + + this._register(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('scm.diffDecorationsGutterPattern')) { + this.onDidChange(); + } + })); + + this._register(Event.runAndSubscribe(this.quickDiffModelRef.object.onDidChange, () => this.onDidChange())); + } + + private onDidChange(): void { + if (!this.codeEditor.hasModel()) { + return; + } + + const visibleQuickDiffs = this.quickDiffModelRef.object.quickDiffs.filter(quickDiff => quickDiff.visible); + const pattern = this.configurationService.getValue<{ added: boolean; modified: boolean }>('scm.diffDecorationsGutterPattern'); + + const decorations = this.quickDiffModelRef.object.changes + .filter(labeledChange => visibleQuickDiffs.some(quickDiff => quickDiff.label === labeledChange.label)) + .map((labeledChange) => { + const change = labeledChange.change; + const changeType = getChangeType(change); + const startLineNumber = change.modifiedStartLineNumber; + const endLineNumber = change.modifiedEndLineNumber || startLineNumber; + + switch (changeType) { + case ChangeType.Add: + return { + range: { + startLineNumber: startLineNumber, startColumn: 1, + endLineNumber: endLineNumber, endColumn: 1 + }, + options: pattern.added ? this.addedPatternOptions : this.addedOptions + }; + case ChangeType.Delete: + return { + range: { + startLineNumber: startLineNumber, startColumn: Number.MAX_VALUE, + endLineNumber: startLineNumber, endColumn: Number.MAX_VALUE + }, + options: this.deletedOptions + }; + case ChangeType.Modify: + return { + range: { + startLineNumber: startLineNumber, startColumn: 1, + endLineNumber: endLineNumber, endColumn: 1 + }, + options: pattern.modified ? this.modifiedPatternOptions : this.modifiedOptions + }; + } + }); + + if (!this.decorationsCollection) { + this.decorationsCollection = this.codeEditor.createDecorationsCollection(decorations); + } else { + this.decorationsCollection.set(decorations); + } + } + + override dispose(): void { + if (this.decorationsCollection) { + this.decorationsCollection?.clear(); + } + this.decorationsCollection = undefined; + this.quickDiffModelRef.dispose(); + super.dispose(); + } +} + +interface QuickDiffWorkbenchControllerViewState { + readonly width: number; + readonly visibility: 'always' | 'hover'; +} + +export class QuickDiffWorkbenchController extends Disposable implements IWorkbenchContribution { + + private enabled = false; + + // Resource URI -> Code Editor Id -> Decoration (Disposable) + private readonly decorators = new ResourceMap>(); + private viewState: QuickDiffWorkbenchControllerViewState = { width: 3, visibility: 'always' }; + private readonly transientDisposables = this._register(new DisposableStore()); + private readonly stylesheet: HTMLStyleElement; + + constructor( + @IEditorService private readonly editorService: IEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IQuickDiffModelService private readonly quickDiffModelService: IQuickDiffModelService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + ) { + super(); + this.stylesheet = domStylesheetsJs.createStyleSheet(undefined, undefined, this._store); + + const onDidChangeConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorations')); + this._register(onDidChangeConfiguration(this.onDidChangeConfiguration, this)); + this.onDidChangeConfiguration(); + + const onDidChangeDiffWidthConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorationsGutterWidth')); + this._register(onDidChangeDiffWidthConfiguration(this.onDidChangeDiffWidthConfiguration, this)); + this.onDidChangeDiffWidthConfiguration(); + + const onDidChangeDiffVisibilityConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorationsGutterVisibility')); + this._register(onDidChangeDiffVisibilityConfiguration(this.onDidChangeDiffVisibilityConfiguration, this)); + this.onDidChangeDiffVisibilityConfiguration(); + } + + private onDidChangeConfiguration(): void { + const enabled = this.configurationService.getValue('scm.diffDecorations') !== 'none'; + + if (enabled) { + this.enable(); + } else { + this.disable(); + } + } + + private onDidChangeDiffWidthConfiguration(): void { + let width = this.configurationService.getValue('scm.diffDecorationsGutterWidth'); + + if (isNaN(width) || width <= 0 || width > 5) { + width = 3; + } + + this.setViewState({ ...this.viewState, width }); + } + + private onDidChangeDiffVisibilityConfiguration(): void { + const visibility = this.configurationService.getValue<'always' | 'hover'>('scm.diffDecorationsGutterVisibility'); + this.setViewState({ ...this.viewState, visibility }); + } + + private setViewState(state: QuickDiffWorkbenchControllerViewState): void { + this.viewState = state; + this.stylesheet.textContent = ` + .monaco-editor .dirty-diff-added, + .monaco-editor .dirty-diff-modified { + border-left-width:${state.width}px; + } + .monaco-editor .dirty-diff-added-pattern, + .monaco-editor .dirty-diff-added-pattern:before, + .monaco-editor .dirty-diff-modified-pattern, + .monaco-editor .dirty-diff-modified-pattern:before { + background-size: ${state.width}px ${state.width}px; + } + .monaco-editor .dirty-diff-added, + .monaco-editor .dirty-diff-added-pattern, + .monaco-editor .dirty-diff-modified, + .monaco-editor .dirty-diff-modified-pattern, + .monaco-editor .dirty-diff-deleted { + opacity: ${state.visibility === 'always' ? 1 : 0}; + } + `; + } + + private enable(): void { + if (this.enabled) { + this.disable(); + } + + this.transientDisposables.add(Event.any(this.editorService.onDidCloseEditor, this.editorService.onDidVisibleEditorsChange)(() => this.onEditorsChanged())); + this.onEditorsChanged(); + this.enabled = true; + } + + private disable(): void { + if (!this.enabled) { + return; + } + + this.transientDisposables.clear(); + + for (const [uri, decoratorMap] of this.decorators.entries()) { + decoratorMap.dispose(); + this.decorators.delete(uri); + } + + this.enabled = false; + } + + private onEditorsChanged(): void { + for (const editor of this.editorService.visibleTextEditorControls) { + if (!isCodeEditor(editor)) { + continue; + } + + const textModel = editor.getModel(); + if (!textModel) { + continue; + } + + const editorId = editor.getId(); + if (this.decorators.get(textModel.uri)?.has(editorId)) { + continue; + } + + const quickDiffModelRef = this.quickDiffModelService.createQuickDiffModelReference(textModel.uri); + if (!quickDiffModelRef) { + continue; + } + + if (!this.decorators.has(textModel.uri)) { + this.decorators.set(textModel.uri, new DisposableMap()); + } + + this.decorators.get(textModel.uri)!.set(editorId, new QuickDiffDecorator(editor, quickDiffModelRef, this.configurationService)); + } + + // Dispose decorators for editors that are no longer visible. + for (const [uri, decoratorMap] of this.decorators.entries()) { + for (const editorId of decoratorMap.keys()) { + const codeEditor = this.editorService.visibleTextEditorControls + .find(editor => isCodeEditor(editor) && editor.getId() === editorId && + this.uriIdentityService.extUri.isEqual(editor.getModel()?.uri, uri)); + + if (!codeEditor) { + decoratorMap.deleteAndDispose(editorId); + } + } + + if (decoratorMap.size === 0) { + decoratorMap.dispose(); + this.decorators.delete(uri); + } + } + } + + override dispose(): void { + this.disable(); + super.dispose(); + } +} diff --git a/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts similarity index 79% rename from src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts rename to src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index 9d9c1d395aa..e7f97e04082 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtyDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -13,7 +13,7 @@ import { URI } from '../../../../base/common/uri.js'; import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; import { ITextModel, shouldSynchronizeModel } from '../../../../editor/common/model.js'; -import { IQuickDiffService, QuickDiff, QuickDiffChange, QuickDiffResult } from '../common/quickDiff.js'; +import { compareChanges, getModifiedEndLineNumber, IQuickDiffService, QuickDiff, QuickDiffChange, QuickDiffResult } from '../common/quickDiff.js'; import { ThrottledDelayer } from '../../../../base/common/async.js'; import { ISCMRepository, ISCMService } from '../common/scm.js'; import { sortedDiff, equals } from '../../../../base/common/arrays.js'; @@ -29,83 +29,66 @@ import { IProgressService, ProgressLocation } from '../../../../platform/progres import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -export const IDirtyDiffModelService = createDecorator('IDirtyDiffModelService'); +export const IQuickDiffModelService = createDecorator('IQuickDiffModelService'); -export interface IDirtyDiffModelService { - _serviceBrand: undefined; +export interface QuickDiffModelOptions { + readonly algorithm: DiffAlgorithmName; +} - /** - * Returns `undefined` if the editor model is not resolved. - * Model refrence has to be disposed once not needed anymore. - * @param resource - * @param algorithm - */ - createDiffModelReference(resource: URI, algorithm: DiffAlgorithmName): IReference | undefined; +export interface IQuickDiffModelService { + _serviceBrand: undefined; /** * Returns `undefined` if the editor model is not resolved. * Model refrence has to be disposed once not needed anymore. * @param resource + * @param options */ - createDirtyDiffModelReference(resource: URI): IReference | undefined; + createQuickDiffModelReference(resource: URI, options?: QuickDiffModelOptions): IReference | undefined; } -class DirtyDiffModelReferenceCollection extends ReferenceCollection { +class QuickDiffModelReferenceCollection extends ReferenceCollection { constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { super(); } - protected override createReferencedObject(_key: string, textFileModel: IResolvedTextFileEditorModel, algorithm: DiffAlgorithmName | undefined): DirtyDiffModel { - return this._instantiationService.createInstance(DirtyDiffModel, textFileModel, algorithm); + protected override createReferencedObject(_key: string, textFileModel: IResolvedTextFileEditorModel, options?: QuickDiffModelOptions): QuickDiffModel { + return this._instantiationService.createInstance(QuickDiffModel, textFileModel, options); } - protected override destroyReferencedObject(_key: string, object: DirtyDiffModel): void { + protected override destroyReferencedObject(_key: string, object: QuickDiffModel): void { object.dispose(); } } -export class DirtyDiffModelService implements IDirtyDiffModelService { +export class QuickDiffModelService implements IQuickDiffModelService { _serviceBrand: undefined; - private readonly _references: DirtyDiffModelReferenceCollection; + private readonly _references: QuickDiffModelReferenceCollection; constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @ITextFileService private readonly textFileService: ITextFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { - this._references = this.instantiationService.createInstance(DirtyDiffModelReferenceCollection); - } - - createDiffModelReference(resource: URI, algorithm: DiffAlgorithmName): IReference | undefined { - const textFileModel = this.textFileService.files.get(resource); - if (!textFileModel?.isResolved()) { - return undefined; - } - - return this._createModelReference(resource, textFileModel, algorithm); + this._references = this.instantiationService.createInstance(QuickDiffModelReferenceCollection); } - createDirtyDiffModelReference(resource: URI): IReference | undefined { + createQuickDiffModelReference(resource: URI, options?: QuickDiffModelOptions): IReference | undefined { const textFileModel = this.textFileService.files.get(resource); if (!textFileModel?.isResolved()) { return undefined; } - return this._createModelReference(resource, textFileModel, undefined); - } - - private _createModelReference(resource: URI, textFileModel: IResolvedTextFileEditorModel, algorithm: DiffAlgorithmName | undefined): IReference { - resource = algorithm === undefined + resource = options === undefined ? this.uriIdentityService.asCanonicalUri(resource) - : this.uriIdentityService.asCanonicalUri(resource) - .with({ query: `algorithm=${algorithm}` }); + : this.uriIdentityService.asCanonicalUri(resource).with({ query: JSON.stringify(options) }); - return this._references.acquire(resource.toString(), textFileModel, algorithm); + return this._references.acquire(resource.toString(), textFileModel, options); } } -export class DirtyDiffModel extends Disposable { +export class QuickDiffModel extends Disposable { private _model: ITextFileEditorModel; @@ -136,7 +119,7 @@ export class DirtyDiffModel extends Disposable { constructor( textFileModel: IResolvedTextFileEditorModel, - private readonly algorithm: DiffAlgorithmName | undefined, + private readonly options: QuickDiffModelOptions | undefined, @ISCMService private readonly scmService: ISCMService, @IQuickDiffService private readonly quickDiffService: IQuickDiffService, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, @@ -263,15 +246,15 @@ export class DirtyDiffModel extends Disposable { const allDiffs: QuickDiffChange[] = []; for (const quickDiff of filteredToDiffable) { - const dirtyDiff = await this._diff(quickDiff.originalResource, this._model.resource, ignoreTrimWhitespace); - if (dirtyDiff.changes && dirtyDiff.changes2 && dirtyDiff.changes.length === dirtyDiff.changes2.length) { - for (let index = 0; index < dirtyDiff.changes.length; index++) { + const diff = await this._diff(quickDiff.originalResource, this._model.resource, ignoreTrimWhitespace); + if (diff.changes && diff.changes2 && diff.changes.length === diff.changes2.length) { + for (let index = 0; index < diff.changes.length; index++) { allDiffs.push({ label: quickDiff.label, original: quickDiff.originalResource, modified: this._model.resource, - change: dirtyDiff.changes[index], - change2: dirtyDiff.changes2[index] + change: diff.changes[index], + change2: diff.changes2[index] }); } } @@ -290,7 +273,7 @@ export class DirtyDiffModel extends Disposable { } private async _diff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<{ changes: readonly IChange[] | null; changes2: readonly LineRangeMapping[] | null }> { - if (this.algorithm === undefined) { + if (this.options?.algorithm === undefined) { const changes = await this.editorWorkerService.computeDirtyDiff(original, modified, ignoreTrimWhitespace); return { changes, changes2: changes?.map(change => lineRangeMappingFromChange(change)) ?? null }; } @@ -299,7 +282,7 @@ export class DirtyDiffModel extends Disposable { computeMoves: false, ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER - }, this.algorithm); + }, this.options.algorithm); return { changes: result ? toLineChanges(DiffState.fromDiffResult(result)) : null, changes2: result?.changes ?? null }; } @@ -377,10 +360,10 @@ export class DirtyDiffModel extends Disposable { const quickDiffs = await this.quickDiffService.getQuickDiffs(uri, this._model.getLanguageId(), isSynchronized); // TODO@lszomoru - find a long term solution for this - // When the DirtyDiffModel is created for a diff editor, there is no + // When the QuickDiffModel is created for a diff editor, there is no // need to compute the diff information for the `isSCM` quick diff // provider as that information will be provided by the diff editor - return this.algorithm !== undefined + return this.options?.algorithm !== undefined ? quickDiffs.filter(quickDiff => !quickDiff.isSCM) : quickDiffs; } @@ -464,55 +447,3 @@ export class DirtyDiffModel extends Disposable { super.dispose(); } } - -function compareChanges(a: IChange, b: IChange): number { - let result = a.modifiedStartLineNumber - b.modifiedStartLineNumber; - - if (result !== 0) { - return result; - } - - result = a.modifiedEndLineNumber - b.modifiedEndLineNumber; - - if (result !== 0) { - return result; - } - - result = a.originalStartLineNumber - b.originalStartLineNumber; - - if (result !== 0) { - return result; - } - - return a.originalEndLineNumber - b.originalEndLineNumber; -} - -export function getChangeHeight(change: IChange): number { - const modified = change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1; - const original = change.originalEndLineNumber - change.originalStartLineNumber + 1; - - if (change.originalEndLineNumber === 0) { - return modified; - } else if (change.modifiedEndLineNumber === 0) { - return original; - } else { - return modified + original; - } -} - -export function getModifiedEndLineNumber(change: IChange): number { - if (change.modifiedEndLineNumber === 0) { - return change.modifiedStartLineNumber === 0 ? 1 : change.modifiedStartLineNumber; - } else { - return change.modifiedEndLineNumber; - } -} - -export function lineIntersectsChange(lineNumber: number, change: IChange): boolean { - // deletion at the beginning of the file - if (lineNumber === 1 && change.modifiedStartLineNumber === 0 && change.modifiedEndLineNumber === 0) { - return true; - } - - return lineNumber >= change.modifiedStartLineNumber && lineNumber <= (change.modifiedEndLineNumber || change.modifiedStartLineNumber); -} diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts similarity index 58% rename from src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts rename to src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index f14fd38555f..35771008fe6 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -4,58 +4,112 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from '../../../../nls.js'; - -import './media/dirtydiffDecorator.css'; -import { IDisposable, toDisposable, Disposable, DisposableStore, DisposableMap, IReference } from '../../../../base/common/lifecycle.js'; +import * as dom from '../../../../base/browser/dom.js'; +import * as domStylesheetsJs from '../../../../base/browser/domStylesheets.js'; +import { Action, ActionRunner, IAction } from '../../../../base/common/actions.js'; import { Event } from '../../../../base/common/event.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; +import { ISelectOptionItem } from '../../../../base/browser/ui/selectBox/selectBox.js'; +import { SelectActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { defaultSelectBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; +import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { getOuterEditor, peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from '../../../../editor/contrib/peekView/browser/peekView.js'; +import { editorBackground } from '../../../../platform/theme/common/colorRegistry.js'; +import { IMenu, IMenuService, MenuId, MenuItemAction, MenuRegistry } from '../../../../platform/actions/common/actions.js'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; +import { EditorAction, registerEditorAction } from '../../../../editor/browser/editorExtensions.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; +import { EmbeddedDiffEditorWidget } from '../../../../editor/browser/widget/diffEditor/embeddedDiffEditorWidget.js'; +import { IEditorContribution, ScrollType } from '../../../../editor/common/editorCommon.js'; +import { IQuickDiffModelService, QuickDiffModel } from './quickDiffModel.js'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { URI } from '../../../../base/common/uri.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; -import { IColorTheme, themeColorFromId, IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { editorErrorForeground, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; -import { ICodeEditor, IEditorMouseEvent, isCodeEditor, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; -import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorContributionInstantiation } from '../../../../editor/browser/editorExtensions.js'; -import { PeekViewWidget, getOuterEditor, peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground } from '../../../../editor/contrib/peekView/browser/peekView.js'; -import { IContextKeyService, IContextKey, ContextKeyExpr, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; import { rot } from '../../../../base/common/numbers.js'; -import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { EmbeddedDiffEditorWidget } from '../../../../editor/browser/widget/diffEditor/embeddedDiffEditorWidget.js'; -import { IDiffEditorOptions, EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { Action, IAction, ActionRunner } from '../../../../base/common/actions.js'; -import { IActionBarOptions } from '../../../../base/browser/ui/actionbar/actionbar.js'; -import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { basename } from '../../../../base/common/resources.js'; -import { MenuId, IMenuService, IMenu, MenuItemAction, MenuRegistry } from '../../../../platform/actions/common/actions.js'; -import { getFlatActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; -import { ScrollType, IEditorContribution, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { OverviewRulerLane, IModelDecorationOptions, MinimapPosition } from '../../../../editor/common/model.js'; -import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { ISplice } from '../../../../base/common/sequence.js'; -import * as dom from '../../../../base/browser/dom.js'; -import * as domStylesheetsJs from '../../../../base/browser/domStylesheets.js'; -import { gotoNextLocation, gotoPreviousLocation } from '../../../../platform/theme/common/iconRegistry.js'; -import { Codicon } from '../../../../base/common/codicons.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; +import { ChangeType, getChangeHeight, getChangeType, getChangeTypeColor, getModifiedEndLineNumber, lineIntersectsChange, QuickDiffChange } from '../common/quickDiff.js'; +import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { TextCompareEditorActiveContext } from '../../../common/contextkeys.js'; +import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; +import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; -import { Color } from '../../../../base/common/color.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; -import { IQuickDiffService, QuickDiffChange } from '../common/quickDiff.js'; -import { IQuickDiffSelectItem, SwitchQuickDiffBaseAction, SwitchQuickDiffViewItem } from './dirtyDiffSwitcher.js'; import { Iterable } from '../../../../base/common/iterator.js'; -import { DirtyDiffModel, getChangeHeight, getModifiedEndLineNumber, IDirtyDiffModelService, lineIntersectsChange } from './dirtyDiffModel.js'; -import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { ResourceMap } from '../../../../base/common/map.js'; -import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +import { basename } from '../../../../base/common/resources.js'; +import { EditorOption, IDiffEditorOptions } from '../../../../editor/common/config/editorOptions.js'; +import { Position } from '../../../../editor/common/core/position.js'; +import { Range } from '../../../../editor/common/core/range.js'; +import { getFlatActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { IActionBarOptions } from '../../../../base/browser/ui/actionbar/actionbar.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; +import { gotoNextLocation, gotoPreviousLocation } from '../../../../platform/theme/common/iconRegistry.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { Color } from '../../../../base/common/color.js'; +import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; + +export const isQuickDiffVisible = new RawContextKey('dirtyDiffVisible', false); + +export interface IQuickDiffSelectItem extends ISelectOptionItem { + provider: string; +} + +export class QuickDiffPickerViewItem extends SelectActionViewItem { + private readonly optionsItems: IQuickDiffSelectItem[]; + + constructor( + action: IAction, + providers: string[], + selected: string, + @IContextViewService contextViewService: IContextViewService, + @IThemeService themeService: IThemeService + ) { + const items = providers.map(provider => ({ provider, text: provider })); + let startingSelection = providers.indexOf(selected); + if (startingSelection === -1) { + startingSelection = 0; + } + const styles = { ...defaultSelectBoxStyles }; + const theme = themeService.getColorTheme(); + const editorBackgroundColor = theme.getColor(editorBackground); + const peekTitleColor = theme.getColor(peekViewTitleBackground); + const opaqueTitleColor = peekTitleColor?.makeOpaque(editorBackgroundColor!) ?? editorBackgroundColor!; + styles.selectBackground = opaqueTitleColor.lighten(.6).toString(); + super(null, action, items, startingSelection, contextViewService, styles, { ariaLabel: nls.localize('remotes', 'Switch quick diff base') }); + this.optionsItems = items; + } -class DiffActionRunner extends ActionRunner { + public setSelection(provider: string) { + const index = this.optionsItems.findIndex(item => item.provider === provider); + this.select(index); + } + + protected override getActionContext(_: string, index: number): IQuickDiffSelectItem { + return this.optionsItems[index]; + } + + override render(container: HTMLElement): void { + super.render(container); + this.setFocusable(true); + } +} + +export class QuickDiffPickerBaseAction extends Action { + + public static readonly ID = 'quickDiff.base.switch'; + public static readonly LABEL = nls.localize('quickDiff.base.switch', "Switch Quick Diff Base"); + + constructor(private readonly callback: (event?: IQuickDiffSelectItem) => void) { + super(QuickDiffPickerBaseAction.ID, QuickDiffPickerBaseAction.LABEL, undefined, undefined); + } + + override async run(event?: IQuickDiffSelectItem): Promise { + return this.callback(event); + } +} + +class QuickDiffWidgetActionRunner extends ActionRunner { protected override runAction(action: IAction, context: any): Promise { if (action instanceof MenuItemAction) { @@ -66,9 +120,7 @@ class DiffActionRunner extends ActionRunner { } } -export const isDirtyDiffVisible = new RawContextKey('dirtyDiffVisible', false); - -class UIEditorAction extends Action { +class QuickDiffWidgetEditorAction extends Action { private editor: ICodeEditor; private action: EditorAction; @@ -96,43 +148,7 @@ class UIEditorAction extends Action { } } -enum ChangeType { - Modify, - Add, - Delete -} - -function getChangeType(change: IChange): ChangeType { - if (change.originalEndLineNumber === 0) { - return ChangeType.Add; - } else if (change.modifiedEndLineNumber === 0) { - return ChangeType.Delete; - } else { - return ChangeType.Modify; - } -} - -function getChangeTypeColor(theme: IColorTheme, changeType: ChangeType): Color | undefined { - switch (changeType) { - case ChangeType.Modify: return theme.getColor(editorGutterModifiedBackground); - case ChangeType.Add: return theme.getColor(editorGutterAddedBackground); - case ChangeType.Delete: return theme.getColor(editorGutterDeletedBackground); - } -} - -function getOuterEditorFromDiffEditor(accessor: ServicesAccessor): ICodeEditor | null { - const diffEditors = accessor.get(ICodeEditorService).listDiffEditors(); - - for (const diffEditor of diffEditors) { - if (diffEditor.hasTextFocus() && diffEditor instanceof EmbeddedDiffEditorWidget) { - return diffEditor.getParentEditor(); - } - } - - return getOuterEditor(accessor); -} - -class DirtyDiffWidget extends PeekViewWidget { +class QuickDiffWidget extends PeekViewWidget { private diffEditor!: EmbeddedDiffEditorWidget; private title: string; @@ -141,12 +157,12 @@ class DirtyDiffWidget extends PeekViewWidget { private _provider: string = ''; private change: IChange | undefined; private height: number | undefined = undefined; - private dropdown: SwitchQuickDiffViewItem | undefined; + private dropdown: QuickDiffPickerViewItem | undefined; private dropdownContainer: HTMLElement | undefined; constructor( editor: ICodeEditor, - private model: DirtyDiffModel, + private model: QuickDiffModel, @IThemeService private readonly themeService: IThemeService, @IInstantiationService instantiationService: IInstantiationService, @IMenuService private readonly menuService: IMenuService, @@ -297,8 +313,8 @@ class DirtyDiffWidget extends PeekViewWidget { if (!this._actionbarWidget) { return; } - const previous = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowPreviousChangeAction(this.editor), ThemeIcon.asClassName(gotoPreviousLocation)); - const next = this.instantiationService.createInstance(UIEditorAction, this.editor, new ShowNextChangeAction(this.editor), ThemeIcon.asClassName(gotoNextLocation)); + const previous = this.instantiationService.createInstance(QuickDiffWidgetEditorAction, this.editor, new ShowPreviousChangeAction(this.editor), ThemeIcon.asClassName(gotoPreviousLocation)); + const next = this.instantiationService.createInstance(QuickDiffWidgetEditorAction, this.editor, new ShowNextChangeAction(this.editor), ThemeIcon.asClassName(gotoNextLocation)); this._disposables.add(previous); this._disposables.add(next); @@ -318,18 +334,18 @@ class DirtyDiffWidget extends PeekViewWidget { super._fillHead(container, true); this.dropdownContainer = dom.prepend(this._titleElement!, dom.$('.dropdown')); - this.dropdown = this.instantiationService.createInstance(SwitchQuickDiffViewItem, new SwitchQuickDiffBaseAction((event?: IQuickDiffSelectItem) => this.switchQuickDiff(event)), + this.dropdown = this.instantiationService.createInstance(QuickDiffPickerViewItem, new QuickDiffPickerBaseAction((event?: IQuickDiffSelectItem) => this.switchQuickDiff(event)), this.model.quickDiffs.map(quickDiffer => quickDiffer.label), this.model.changes[this._index].label); this.dropdown.render(this.dropdownContainer); this.updateActions(); } protected override _getActionBarOptions(): IActionBarOptions { - const actionRunner = new DiffActionRunner(); + const actionRunner = new QuickDiffWidgetActionRunner(); // close widget on successful action actionRunner.onDidRun(e => { - if (!(e.action instanceof UIEditorAction) && !e.error) { + if (!(e.action instanceof QuickDiffWidgetEditorAction) && !e.error) { this.dispose(); } }); @@ -426,231 +442,17 @@ class DirtyDiffWidget extends PeekViewWidget { } } -export class ShowPreviousChangeAction extends EditorAction { - - constructor(private readonly outerEditor?: ICodeEditor) { - super({ - id: 'editor.action.dirtydiff.previous', - label: nls.localize2('show previous change', "Show Previous Change"), - precondition: TextCompareEditorActiveContext.toNegated(), - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } - }); - } - - run(accessor: ServicesAccessor): void { - const outerEditor = this.outerEditor ?? getOuterEditorFromDiffEditor(accessor); - - if (!outerEditor) { - return; - } - - const controller = DirtyDiffController.get(outerEditor); - - if (!controller) { - return; - } +export class QuickDiffEditorController extends Disposable implements IEditorContribution { - if (!controller.canNavigate()) { - return; - } + public static readonly ID = 'editor.contrib.quickdiff'; - controller.previous(); + static get(editor: ICodeEditor): QuickDiffEditorController | null { + return editor.getContribution(QuickDiffEditorController.ID); } -} -registerEditorAction(ShowPreviousChangeAction); - -export class ShowNextChangeAction extends EditorAction { - constructor(private readonly outerEditor?: ICodeEditor) { - super({ - id: 'editor.action.dirtydiff.next', - label: nls.localize2('show next change', "Show Next Change"), - precondition: TextCompareEditorActiveContext.toNegated(), - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } - }); - } - - run(accessor: ServicesAccessor): void { - const outerEditor = this.outerEditor ?? getOuterEditorFromDiffEditor(accessor); - - if (!outerEditor) { - return; - } - - const controller = DirtyDiffController.get(outerEditor); - - if (!controller) { - return; - } - - if (!controller.canNavigate()) { - return; - } - - controller.next(); - } -} -registerEditorAction(ShowNextChangeAction); - -// Go to menu -MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '7_change_nav', - command: { - id: 'editor.action.dirtydiff.next', - title: nls.localize({ key: 'miGotoNextChange', comment: ['&& denotes a mnemonic'] }, "Next &&Change") - }, - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '7_change_nav', - command: { - id: 'editor.action.dirtydiff.previous', - title: nls.localize({ key: 'miGotoPreviousChange', comment: ['&& denotes a mnemonic'] }, "Previous &&Change") - }, - order: 2 -}); - -export class GotoPreviousChangeAction extends EditorAction { - - constructor() { - super({ - id: 'workbench.action.editor.previousChange', - label: nls.localize2('move to previous change', "Go to Previous Change"), - precondition: TextCompareEditorActiveContext.toNegated(), - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } - }); - } - - async run(accessor: ServicesAccessor): Promise { - const outerEditor = getOuterEditorFromDiffEditor(accessor); - const accessibilitySignalService = accessor.get(IAccessibilitySignalService); - const accessibilityService = accessor.get(IAccessibilityService); - const codeEditorService = accessor.get(ICodeEditorService); - const dirtyDiffModelService = accessor.get(IDirtyDiffModelService); - - if (!outerEditor || !outerEditor.hasModel()) { - return; - } - - const modelRef = dirtyDiffModelService.createDirtyDiffModelReference(outerEditor.getModel().uri); - try { - if (!modelRef || modelRef.object.changes.length === 0) { - return; - } - - const lineNumber = outerEditor.getPosition().lineNumber; - const index = modelRef.object.findPreviousClosestChange(lineNumber, false); - const change = modelRef.object.changes[index]; - await playAccessibilitySymbolForChange(change.change, accessibilitySignalService); - setPositionAndSelection(change.change, outerEditor, accessibilityService, codeEditorService); - } finally { - modelRef?.dispose(); - } - } -} -registerEditorAction(GotoPreviousChangeAction); - -export class GotoNextChangeAction extends EditorAction { - - constructor() { - super({ - id: 'workbench.action.editor.nextChange', - label: nls.localize2('move to next change', "Go to Next Change"), - precondition: TextCompareEditorActiveContext.toNegated(), - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } - }); - } - - async run(accessor: ServicesAccessor): Promise { - const accessibilitySignalService = accessor.get(IAccessibilitySignalService); - const outerEditor = getOuterEditorFromDiffEditor(accessor); - const accessibilityService = accessor.get(IAccessibilityService); - const codeEditorService = accessor.get(ICodeEditorService); - const dirtyDiffModelService = accessor.get(IDirtyDiffModelService); - - if (!outerEditor || !outerEditor.hasModel()) { - return; - } - - const modelRef = dirtyDiffModelService.createDirtyDiffModelReference(outerEditor.getModel().uri); - try { - if (!modelRef || modelRef.object.changes.length === 0) { - return; - } - - const lineNumber = outerEditor.getPosition().lineNumber; - const index = modelRef.object.findNextClosestChange(lineNumber, false); - const change = modelRef.object.changes[index].change; - await playAccessibilitySymbolForChange(change, accessibilitySignalService); - setPositionAndSelection(change, outerEditor, accessibilityService, codeEditorService); - } finally { - modelRef?.dispose(); - } - } -} - -function setPositionAndSelection(change: IChange, editor: ICodeEditor, accessibilityService: IAccessibilityService, codeEditorService: ICodeEditorService) { - const position = new Position(change.modifiedStartLineNumber, 1); - editor.setPosition(position); - editor.revealPositionInCenter(position); - if (accessibilityService.isScreenReaderOptimized()) { - editor.setSelection({ startLineNumber: change.modifiedStartLineNumber, startColumn: 0, endLineNumber: change.modifiedStartLineNumber, endColumn: Number.MAX_VALUE }); - codeEditorService.getActiveCodeEditor()?.writeScreenReaderContent('diff-navigation'); - } -} - -async function playAccessibilitySymbolForChange(change: IChange, accessibilitySignalService: IAccessibilitySignalService) { - const changeType = getChangeType(change); - switch (changeType) { - case ChangeType.Add: - accessibilitySignalService.playSignal(AccessibilitySignal.diffLineInserted, { allowManyInParallel: true, source: 'dirtyDiffDecoration' }); - break; - case ChangeType.Delete: - accessibilitySignalService.playSignal(AccessibilitySignal.diffLineDeleted, { allowManyInParallel: true, source: 'dirtyDiffDecoration' }); - break; - case ChangeType.Modify: - accessibilitySignalService.playSignal(AccessibilitySignal.diffLineModified, { allowManyInParallel: true, source: 'dirtyDiffDecoration' }); - break; - } -} - -registerEditorAction(GotoNextChangeAction); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'closeDirtyDiff', - weight: KeybindingWeight.EditorContrib + 50, - primary: KeyCode.Escape, - secondary: [KeyMod.Shift | KeyCode.Escape], - when: ContextKeyExpr.and(isDirtyDiffVisible), - handler: (accessor: ServicesAccessor) => { - const outerEditor = getOuterEditorFromDiffEditor(accessor); - - if (!outerEditor) { - return; - } - - const controller = DirtyDiffController.get(outerEditor); - - if (!controller) { - return; - } - - controller.close(); - } -}); - -export class DirtyDiffController extends Disposable implements IEditorContribution { - - public static readonly ID = 'editor.contrib.dirtydiff'; - - static get(editor: ICodeEditor): DirtyDiffController | null { - return editor.getContribution(DirtyDiffController.ID); - } - - private model: DirtyDiffModel | null = null; - private widget: DirtyDiffWidget | null = null; - private readonly isDirtyDiffVisible!: IContextKey; + private model: QuickDiffModel | null = null; + private widget: QuickDiffWidget | null = null; + private readonly isQuickDiffVisible!: IContextKey; private session: IDisposable = Disposable.None; private mouseDownInfo: { lineNumber: number } | null = null; private enabled = false; @@ -661,7 +463,7 @@ export class DirtyDiffController extends Disposable implements IEditorContributi private editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IDirtyDiffModelService private readonly dirtyDiffModelService: IDirtyDiffModelService, + @IQuickDiffModelService private readonly quickDiffModelService: IQuickDiffModelService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -669,7 +471,7 @@ export class DirtyDiffController extends Disposable implements IEditorContributi this.stylesheet = domStylesheetsJs.createStyleSheet(undefined, undefined, this._store); if (this.enabled) { - this.isDirtyDiffVisible = isDirtyDiffVisible.bindTo(contextKeyService); + this.isQuickDiffVisible = isQuickDiffVisible.bindTo(contextKeyService); this._register(editor.onDidChangeModel(() => this.close())); const onDidChangeGutterAction = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorationsGutterAction')); @@ -781,7 +583,7 @@ export class DirtyDiffController extends Disposable implements IEditorContributi return false; } - const modelRef = this.dirtyDiffModelService.createDirtyDiffModelReference(editorModel.uri); + const modelRef = this.quickDiffModelService.createQuickDiffModelReference(editorModel.uri); if (!modelRef) { return false; @@ -793,8 +595,8 @@ export class DirtyDiffController extends Disposable implements IEditorContributi } this.model = modelRef.object; - this.widget = this.instantiationService.createInstance(DirtyDiffWidget, this.editor, this.model); - this.isDirtyDiffVisible.set(true); + this.widget = this.instantiationService.createInstance(QuickDiffWidget, this.editor, this.model); + this.isQuickDiffVisible.set(true); const disposables = new DisposableStore(); disposables.add(Event.once(this.widget.onDidClose)(this.close, this)); @@ -810,7 +612,7 @@ export class DirtyDiffController extends Disposable implements IEditorContributi disposables.add(toDisposable(() => { this.model = null; this.widget = null; - this.isDirtyDiffVisible.set(false); + this.isQuickDiffVisible.set(false); this.editor.focus(); })); @@ -892,7 +694,7 @@ export class DirtyDiffController extends Disposable implements IEditorContributi return; } - const modelRef = this.dirtyDiffModelService.createDirtyDiffModelReference(editorModel.uri); + const modelRef = this.quickDiffModelService.createQuickDiffModelReference(editorModel.uri); if (!modelRef) { return; @@ -922,350 +724,226 @@ export class DirtyDiffController extends Disposable implements IEditorContributi } } -const editorGutterModifiedBackground = registerColor('editorGutter.modifiedBackground', { - dark: '#1B81A8', - light: '#2090D3', - hcDark: '#1B81A8', - hcLight: '#2090D3' -}, nls.localize('editorGutterModifiedBackground', "Editor gutter background color for lines that are modified.")); - -const editorGutterAddedBackground = registerColor('editorGutter.addedBackground', { - dark: '#487E02', - light: '#48985D', - hcDark: '#487E02', - hcLight: '#48985D' -}, nls.localize('editorGutterAddedBackground', "Editor gutter background color for lines that are added.")); - -const editorGutterDeletedBackground = registerColor('editorGutter.deletedBackground', editorErrorForeground, nls.localize('editorGutterDeletedBackground', "Editor gutter background color for lines that are deleted.")); - -export const minimapGutterModifiedBackground = registerColor('minimapGutter.modifiedBackground', editorGutterModifiedBackground, nls.localize('minimapGutterModifiedBackground', "Minimap gutter background color for lines that are modified.")); - -export const minimapGutterAddedBackground = registerColor('minimapGutter.addedBackground', editorGutterAddedBackground, nls.localize('minimapGutterAddedBackground', "Minimap gutter background color for lines that are added.")); - -export const minimapGutterDeletedBackground = registerColor('minimapGutter.deletedBackground', editorGutterDeletedBackground, nls.localize('minimapGutterDeletedBackground', "Minimap gutter background color for lines that are deleted.")); - -export const overviewRulerModifiedForeground = registerColor('editorOverviewRuler.modifiedForeground', transparent(editorGutterModifiedBackground, 0.6), nls.localize('overviewRulerModifiedForeground', 'Overview ruler marker color for modified content.')); -export const overviewRulerAddedForeground = registerColor('editorOverviewRuler.addedForeground', transparent(editorGutterAddedBackground, 0.6), nls.localize('overviewRulerAddedForeground', 'Overview ruler marker color for added content.')); -export const overviewRulerDeletedForeground = registerColor('editorOverviewRuler.deletedForeground', transparent(editorGutterDeletedBackground, 0.6), nls.localize('overviewRulerDeletedForeground', 'Overview ruler marker color for deleted content.')); +export class ShowPreviousChangeAction extends EditorAction { -class DirtyDiffDecorator extends Disposable { + constructor(private readonly outerEditor?: ICodeEditor) { + super({ + id: 'editor.action.dirtydiff.previous', + label: nls.localize2('show previous change', "Show Previous Change"), + precondition: TextCompareEditorActiveContext.toNegated(), + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } + }); + } - static createDecoration(className: string, tooltip: string | null, options: { gutter: boolean; overview: { active: boolean; color: string }; minimap: { active: boolean; color: string }; isWholeLine: boolean }): ModelDecorationOptions { - const decorationOptions: IModelDecorationOptions = { - description: 'dirty-diff-decoration', - isWholeLine: options.isWholeLine, - }; + run(accessor: ServicesAccessor): void { + const outerEditor = this.outerEditor ?? getOuterEditorFromDiffEditor(accessor); - if (options.gutter) { - decorationOptions.linesDecorationsClassName = `dirty-diff-glyph ${className}`; - decorationOptions.linesDecorationsTooltip = tooltip; + if (!outerEditor) { + return; } - if (options.overview.active) { - decorationOptions.overviewRuler = { - color: themeColorFromId(options.overview.color), - position: OverviewRulerLane.Left - }; + const controller = QuickDiffEditorController.get(outerEditor); + + if (!controller) { + return; } - if (options.minimap.active) { - decorationOptions.minimap = { - color: themeColorFromId(options.minimap.color), - position: MinimapPosition.Gutter - }; + if (!controller.canNavigate()) { + return; } - return ModelDecorationOptions.createDynamic(decorationOptions); + controller.previous(); } +} +registerEditorAction(ShowPreviousChangeAction); - private addedOptions: ModelDecorationOptions; - private addedPatternOptions: ModelDecorationOptions; - private modifiedOptions: ModelDecorationOptions; - private modifiedPatternOptions: ModelDecorationOptions; - private deletedOptions: ModelDecorationOptions; - private decorationsCollection: IEditorDecorationsCollection | undefined; - - constructor( - private readonly codeEditor: ICodeEditor, - private readonly dirtyDiffModelRef: IReference, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(); +export class ShowNextChangeAction extends EditorAction { - const decorations = configurationService.getValue('scm.diffDecorations'); - const gutter = decorations === 'all' || decorations === 'gutter'; - const overview = decorations === 'all' || decorations === 'overview'; - const minimap = decorations === 'all' || decorations === 'minimap'; - - const diffAdded = nls.localize('diffAdded', 'Added lines'); - this.addedOptions = DirtyDiffDecorator.createDecoration('dirty-diff-added', diffAdded, { - gutter, - overview: { active: overview, color: overviewRulerAddedForeground }, - minimap: { active: minimap, color: minimapGutterAddedBackground }, - isWholeLine: true - }); - this.addedPatternOptions = DirtyDiffDecorator.createDecoration('dirty-diff-added-pattern', diffAdded, { - gutter, - overview: { active: overview, color: overviewRulerAddedForeground }, - minimap: { active: minimap, color: minimapGutterAddedBackground }, - isWholeLine: true - }); - const diffModified = nls.localize('diffModified', 'Changed lines'); - this.modifiedOptions = DirtyDiffDecorator.createDecoration('dirty-diff-modified', diffModified, { - gutter, - overview: { active: overview, color: overviewRulerModifiedForeground }, - minimap: { active: minimap, color: minimapGutterModifiedBackground }, - isWholeLine: true - }); - this.modifiedPatternOptions = DirtyDiffDecorator.createDecoration('dirty-diff-modified-pattern', diffModified, { - gutter, - overview: { active: overview, color: overviewRulerModifiedForeground }, - minimap: { active: minimap, color: minimapGutterModifiedBackground }, - isWholeLine: true - }); - this.deletedOptions = DirtyDiffDecorator.createDecoration('dirty-diff-deleted', nls.localize('diffDeleted', 'Removed lines'), { - gutter, - overview: { active: overview, color: overviewRulerDeletedForeground }, - minimap: { active: minimap, color: minimapGutterDeletedBackground }, - isWholeLine: false + constructor(private readonly outerEditor?: ICodeEditor) { + super({ + id: 'editor.action.dirtydiff.next', + label: nls.localize2('show next change', "Show Next Change"), + precondition: TextCompareEditorActiveContext.toNegated(), + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } }); - - this._register(configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('scm.diffDecorationsGutterPattern')) { - this.onDidChange(); - } - })); - - this._register(Event.runAndSubscribe(this.dirtyDiffModelRef.object.onDidChange, () => this.onDidChange())); } - private onDidChange(): void { - if (!this.codeEditor.hasModel()) { + run(accessor: ServicesAccessor): void { + const outerEditor = this.outerEditor ?? getOuterEditorFromDiffEditor(accessor); + + if (!outerEditor) { return; } - const visibleQuickDiffs = this.dirtyDiffModelRef.object.quickDiffs.filter(quickDiff => quickDiff.visible); - const pattern = this.configurationService.getValue<{ added: boolean; modified: boolean }>('scm.diffDecorationsGutterPattern'); - - const decorations = this.dirtyDiffModelRef.object.changes - .filter(labeledChange => visibleQuickDiffs.some(quickDiff => quickDiff.label === labeledChange.label)) - .map((labeledChange) => { - const change = labeledChange.change; - const changeType = getChangeType(change); - const startLineNumber = change.modifiedStartLineNumber; - const endLineNumber = change.modifiedEndLineNumber || startLineNumber; - - switch (changeType) { - case ChangeType.Add: - return { - range: { - startLineNumber: startLineNumber, startColumn: 1, - endLineNumber: endLineNumber, endColumn: 1 - }, - options: pattern.added ? this.addedPatternOptions : this.addedOptions - }; - case ChangeType.Delete: - return { - range: { - startLineNumber: startLineNumber, startColumn: Number.MAX_VALUE, - endLineNumber: startLineNumber, endColumn: Number.MAX_VALUE - }, - options: this.deletedOptions - }; - case ChangeType.Modify: - return { - range: { - startLineNumber: startLineNumber, startColumn: 1, - endLineNumber: endLineNumber, endColumn: 1 - }, - options: pattern.modified ? this.modifiedPatternOptions : this.modifiedOptions - }; - } - }); + const controller = QuickDiffEditorController.get(outerEditor); - if (!this.decorationsCollection) { - this.decorationsCollection = this.codeEditor.createDecorationsCollection(decorations); - } else { - this.decorationsCollection.set(decorations); + if (!controller) { + return; } - } - override dispose(): void { - if (this.decorationsCollection) { - this.decorationsCollection?.clear(); + if (!controller.canNavigate()) { + return; } - this.decorationsCollection = undefined; - this.dirtyDiffModelRef.dispose(); - super.dispose(); - } -} - -export async function getOriginalResource(quickDiffService: IQuickDiffService, uri: URI, language: string | undefined, isSynchronized: boolean | undefined): Promise { - const quickDiffs = await quickDiffService.getQuickDiffs(uri, language, isSynchronized); - return quickDiffs.length > 0 ? quickDiffs[0].originalResource : null; -} -interface DirtyDiffWorkbenchControllerViewState { - readonly width: number; - readonly visibility: 'always' | 'hover'; + controller.next(); + } } +registerEditorAction(ShowNextChangeAction); -export class DirtyDiffWorkbenchController extends Disposable implements IWorkbenchContribution { - - private enabled = false; - - // Resource URI -> Code Editor Id -> Decoration (Disposable) - private readonly decorators = new ResourceMap>(); - private viewState: DirtyDiffWorkbenchControllerViewState = { width: 3, visibility: 'always' }; - private readonly transientDisposables = this._register(new DisposableStore()); - private readonly stylesheet: HTMLStyleElement; - - constructor( - @IEditorService private readonly editorService: IEditorService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @IDirtyDiffModelService private readonly dirtyDiffModelService: IDirtyDiffModelService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - ) { - super(); - this.stylesheet = domStylesheetsJs.createStyleSheet(undefined, undefined, this._store); - - const onDidChangeConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorations')); - this._register(onDidChangeConfiguration(this.onDidChangeConfiguration, this)); - this.onDidChangeConfiguration(); - - const onDidChangeDiffWidthConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorationsGutterWidth')); - this._register(onDidChangeDiffWidthConfiguration(this.onDidChangeDiffWidthConfiguration, this)); - this.onDidChangeDiffWidthConfiguration(); +export class GotoPreviousChangeAction extends EditorAction { - const onDidChangeDiffVisibilityConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorationsGutterVisibility')); - this._register(onDidChangeDiffVisibilityConfiguration(this.onDidChangeDiffVisibilityConfiguration, this)); - this.onDidChangeDiffVisibilityConfiguration(); + constructor() { + super({ + id: 'workbench.action.editor.previousChange', + label: nls.localize2('move to previous change', "Go to Previous Change"), + precondition: TextCompareEditorActiveContext.toNegated(), + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } + }); } - private onDidChangeConfiguration(): void { - const enabled = this.configurationService.getValue('scm.diffDecorations') !== 'none'; + async run(accessor: ServicesAccessor): Promise { + const outerEditor = getOuterEditorFromDiffEditor(accessor); + const accessibilitySignalService = accessor.get(IAccessibilitySignalService); + const accessibilityService = accessor.get(IAccessibilityService); + const codeEditorService = accessor.get(ICodeEditorService); + const quickDiffModelService = accessor.get(IQuickDiffModelService); - if (enabled) { - this.enable(); - } else { - this.disable(); + if (!outerEditor || !outerEditor.hasModel()) { + return; } - } - private onDidChangeDiffWidthConfiguration(): void { - let width = this.configurationService.getValue('scm.diffDecorationsGutterWidth'); + const modelRef = quickDiffModelService.createQuickDiffModelReference(outerEditor.getModel().uri); + try { + if (!modelRef || modelRef.object.changes.length === 0) { + return; + } - if (isNaN(width) || width <= 0 || width > 5) { - width = 3; + const lineNumber = outerEditor.getPosition().lineNumber; + const index = modelRef.object.findPreviousClosestChange(lineNumber, false); + const change = modelRef.object.changes[index]; + await playAccessibilitySymbolForChange(change.change, accessibilitySignalService); + setPositionAndSelection(change.change, outerEditor, accessibilityService, codeEditorService); + } finally { + modelRef?.dispose(); } - - this.setViewState({ ...this.viewState, width }); } +} +registerEditorAction(GotoPreviousChangeAction); - private onDidChangeDiffVisibilityConfiguration(): void { - const visibility = this.configurationService.getValue<'always' | 'hover'>('scm.diffDecorationsGutterVisibility'); - this.setViewState({ ...this.viewState, visibility }); - } +export class GotoNextChangeAction extends EditorAction { - private setViewState(state: DirtyDiffWorkbenchControllerViewState): void { - this.viewState = state; - this.stylesheet.textContent = ` - .monaco-editor .dirty-diff-added, - .monaco-editor .dirty-diff-modified { - border-left-width:${state.width}px; - } - .monaco-editor .dirty-diff-added-pattern, - .monaco-editor .dirty-diff-added-pattern:before, - .monaco-editor .dirty-diff-modified-pattern, - .monaco-editor .dirty-diff-modified-pattern:before { - background-size: ${state.width}px ${state.width}px; - } - .monaco-editor .dirty-diff-added, - .monaco-editor .dirty-diff-added-pattern, - .monaco-editor .dirty-diff-modified, - .monaco-editor .dirty-diff-modified-pattern, - .monaco-editor .dirty-diff-deleted { - opacity: ${state.visibility === 'always' ? 1 : 0}; - } - `; + constructor() { + super({ + id: 'workbench.action.editor.nextChange', + label: nls.localize2('move to next change', "Go to Next Change"), + precondition: TextCompareEditorActiveContext.toNegated(), + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } + }); } - private enable(): void { - if (this.enabled) { - this.disable(); - } - - this.transientDisposables.add(Event.any(this.editorService.onDidCloseEditor, this.editorService.onDidVisibleEditorsChange)(() => this.onEditorsChanged())); - this.onEditorsChanged(); - this.enabled = true; - } + async run(accessor: ServicesAccessor): Promise { + const accessibilitySignalService = accessor.get(IAccessibilitySignalService); + const outerEditor = getOuterEditorFromDiffEditor(accessor); + const accessibilityService = accessor.get(IAccessibilityService); + const codeEditorService = accessor.get(ICodeEditorService); + const quickDiffModelService = accessor.get(IQuickDiffModelService); - private disable(): void { - if (!this.enabled) { + if (!outerEditor || !outerEditor.hasModel()) { return; } - this.transientDisposables.clear(); + const modelRef = quickDiffModelService.createQuickDiffModelReference(outerEditor.getModel().uri); + try { + if (!modelRef || modelRef.object.changes.length === 0) { + return; + } - for (const [uri, decoratorMap] of this.decorators.entries()) { - decoratorMap.dispose(); - this.decorators.delete(uri); + const lineNumber = outerEditor.getPosition().lineNumber; + const index = modelRef.object.findNextClosestChange(lineNumber, false); + const change = modelRef.object.changes[index].change; + await playAccessibilitySymbolForChange(change, accessibilitySignalService); + setPositionAndSelection(change, outerEditor, accessibilityService, codeEditorService); + } finally { + modelRef?.dispose(); } - - this.enabled = false; } +} +registerEditorAction(GotoNextChangeAction); - private onEditorsChanged(): void { - for (const editor of this.editorService.visibleTextEditorControls) { - if (!isCodeEditor(editor)) { - continue; - } +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '7_change_nav', + command: { + id: 'editor.action.dirtydiff.next', + title: nls.localize({ key: 'miGotoNextChange', comment: ['&& denotes a mnemonic'] }, "Next &&Change") + }, + order: 1 +}); - const textModel = editor.getModel(); - if (!textModel) { - continue; - } +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '7_change_nav', + command: { + id: 'editor.action.dirtydiff.previous', + title: nls.localize({ key: 'miGotoPreviousChange', comment: ['&& denotes a mnemonic'] }, "Previous &&Change") + }, + order: 2 +}); - const editorId = editor.getId(); - if (this.decorators.get(textModel.uri)?.has(editorId)) { - continue; - } +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'closeQuickDiff', + weight: KeybindingWeight.EditorContrib + 50, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(isQuickDiffVisible), + handler: (accessor: ServicesAccessor) => { + const outerEditor = getOuterEditorFromDiffEditor(accessor); - const dirtyDiffModelRef = this.dirtyDiffModelService.createDirtyDiffModelReference(textModel.uri); - if (!dirtyDiffModelRef) { - continue; - } + if (!outerEditor) { + return; + } - if (!this.decorators.has(textModel.uri)) { - this.decorators.set(textModel.uri, new DisposableMap()); - } + const controller = QuickDiffEditorController.get(outerEditor); - this.decorators.get(textModel.uri)!.set(editorId, new DirtyDiffDecorator(editor, dirtyDiffModelRef, this.configurationService)); + if (!controller) { + return; } - // Dispose decorators for editors that are no longer visible. - for (const [uri, decoratorMap] of this.decorators.entries()) { - for (const editorId of decoratorMap.keys()) { - const codeEditor = this.editorService.visibleTextEditorControls - .find(editor => isCodeEditor(editor) && editor.getId() === editorId && - this.uriIdentityService.extUri.isEqual(editor.getModel()?.uri, uri)); - - if (!codeEditor) { - decoratorMap.deleteAndDispose(editorId); - } - } + controller.close(); + } +}); - if (decoratorMap.size === 0) { - decoratorMap.dispose(); - this.decorators.delete(uri); - } - } +function setPositionAndSelection(change: IChange, editor: ICodeEditor, accessibilityService: IAccessibilityService, codeEditorService: ICodeEditorService) { + const position = new Position(change.modifiedStartLineNumber, 1); + editor.setPosition(position); + editor.revealPositionInCenter(position); + if (accessibilityService.isScreenReaderOptimized()) { + editor.setSelection({ startLineNumber: change.modifiedStartLineNumber, startColumn: 0, endLineNumber: change.modifiedStartLineNumber, endColumn: Number.MAX_VALUE }); + codeEditorService.getActiveCodeEditor()?.writeScreenReaderContent('diff-navigation'); } +} - override dispose(): void { - this.disable(); - super.dispose(); +async function playAccessibilitySymbolForChange(change: IChange, accessibilitySignalService: IAccessibilitySignalService) { + const changeType = getChangeType(change); + switch (changeType) { + case ChangeType.Add: + accessibilitySignalService.playSignal(AccessibilitySignal.diffLineInserted, { allowManyInParallel: true, source: 'quickDiffDecoration' }); + break; + case ChangeType.Delete: + accessibilitySignalService.playSignal(AccessibilitySignal.diffLineDeleted, { allowManyInParallel: true, source: 'quickDiffDecoration' }); + break; + case ChangeType.Modify: + accessibilitySignalService.playSignal(AccessibilitySignal.diffLineModified, { allowManyInParallel: true, source: 'quickDiffDecoration' }); + break; } } -registerEditorContribution(DirtyDiffController.ID, DirtyDiffController, EditorContributionInstantiation.AfterFirstRender); +function getOuterEditorFromDiffEditor(accessor: ServicesAccessor): ICodeEditor | null { + const diffEditors = accessor.get(ICodeEditorService).listDiffEditors(); + + for (const diffEditor of diffEditors) { + if (diffEditor.hasTextFocus() && diffEditor instanceof EmbeddedDiffEditorWidget) { + return diffEditor.getParentEditor(); + } + } + + return getOuterEditor(accessor); +} diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index d7e997d29ff..0c2d7c1af5f 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -6,7 +6,7 @@ import { localize, localize2 } from '../../../../nls.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js'; -import { DirtyDiffWorkbenchController } from './dirtydiffDecorator.js'; +import { QuickDiffWorkbenchController } from './quickDiffDecorator.js'; import { VIEWLET_ID, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID, HISTORY_VIEW_PANE_ID } from '../common/scm.js'; import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; import { MenuRegistry, MenuId } from '../../../../platform/actions/common/actions.js'; @@ -40,7 +40,9 @@ import { isSCMRepository } from './util.js'; import { SCMHistoryViewPane } from './scmHistoryViewPane.js'; import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js'; import { RemoteNameContext } from '../../../common/contextkeys.js'; -import { DirtyDiffModelService, IDirtyDiffModelService } from './dirtyDiffModel.js'; +import { QuickDiffModelService, IQuickDiffModelService } from './quickDiffModel.js'; +import { QuickDiffEditorController } from './quickDiffWidget.js'; +import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -50,7 +52,10 @@ ModesRegistry.registerLanguage({ }); Registry.as(WorkbenchExtensions.Workbench) - .registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored); + .registerWorkbenchContribution(QuickDiffWorkbenchController, LifecyclePhase.Restored); + +registerEditorContribution(QuickDiffEditorController.ID, + QuickDiffEditorController, EditorContributionInstantiation.AfterFirstRender); const sourceControlViewIcon = registerIcon('source-control-view-icon', Codicon.sourceControl, localize('sourceControlViewIcon', 'View icon of the Source Control view.')); @@ -597,4 +602,4 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ registerSingleton(ISCMService, SCMService, InstantiationType.Delayed); registerSingleton(ISCMViewService, SCMViewService, InstantiationType.Delayed); registerSingleton(IQuickDiffService, QuickDiffService, InstantiationType.Delayed); -registerSingleton(IDirtyDiffModelService, DirtyDiffModelService, InstantiationType.Delayed); +registerSingleton(IQuickDiffModelService, QuickDiffModelService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/scm/common/quickDiff.ts b/src/vs/workbench/contrib/scm/common/quickDiff.ts index 3770f071c72..55cd69561f0 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiff.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiff.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from '../../../../nls.js'; + import { URI } from '../../../../base/common/uri.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; @@ -10,9 +12,39 @@ import { LanguageSelector } from '../../../../editor/common/languageSelector.js' import { Event } from '../../../../base/common/event.js'; import { LineRangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; +import { IColorTheme } from '../../../../platform/theme/common/themeService.js'; +import { Color } from '../../../../base/common/color.js'; +import { editorErrorForeground, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; export const IQuickDiffService = createDecorator('quickDiff'); +const editorGutterModifiedBackground = registerColor('editorGutter.modifiedBackground', { + dark: '#1B81A8', light: '#2090D3', hcDark: '#1B81A8', hcLight: '#2090D3' +}, nls.localize('editorGutterModifiedBackground', "Editor gutter background color for lines that are modified.")); + +const editorGutterAddedBackground = registerColor('editorGutter.addedBackground', { + dark: '#487E02', light: '#48985D', hcDark: '#487E02', hcLight: '#48985D' +}, nls.localize('editorGutterAddedBackground', "Editor gutter background color for lines that are added.")); + +const editorGutterDeletedBackground = registerColor('editorGutter.deletedBackground', + editorErrorForeground, nls.localize('editorGutterDeletedBackground', "Editor gutter background color for lines that are deleted.")); + +export const minimapGutterModifiedBackground = registerColor('minimapGutter.modifiedBackground', + editorGutterModifiedBackground, nls.localize('minimapGutterModifiedBackground', "Minimap gutter background color for lines that are modified.")); + +export const minimapGutterAddedBackground = registerColor('minimapGutter.addedBackground', + editorGutterAddedBackground, nls.localize('minimapGutterAddedBackground', "Minimap gutter background color for lines that are added.")); + +export const minimapGutterDeletedBackground = registerColor('minimapGutter.deletedBackground', + editorGutterDeletedBackground, nls.localize('minimapGutterDeletedBackground', "Minimap gutter background color for lines that are deleted.")); + +export const overviewRulerModifiedForeground = registerColor('editorOverviewRuler.modifiedForeground', + transparent(editorGutterModifiedBackground, 0.6), nls.localize('overviewRulerModifiedForeground', 'Overview ruler marker color for modified content.')); +export const overviewRulerAddedForeground = registerColor('editorOverviewRuler.addedForeground', + transparent(editorGutterAddedBackground, 0.6), nls.localize('overviewRulerAddedForeground', 'Overview ruler marker color for added content.')); +export const overviewRulerDeletedForeground = registerColor('editorOverviewRuler.deletedForeground', + transparent(editorGutterDeletedBackground, 0.6), nls.localize('overviewRulerDeletedForeground', 'Overview ruler marker color for deleted content.')); + export interface QuickDiffProvider { label: string; rootUri: URI | undefined; @@ -52,3 +84,79 @@ export interface IQuickDiffService { addQuickDiffProvider(quickDiff: QuickDiffProvider): IDisposable; getQuickDiffs(uri: URI, language?: string, isSynchronized?: boolean): Promise; } + +export enum ChangeType { + Modify, + Add, + Delete +} + +export function getChangeType(change: IChange): ChangeType { + if (change.originalEndLineNumber === 0) { + return ChangeType.Add; + } else if (change.modifiedEndLineNumber === 0) { + return ChangeType.Delete; + } else { + return ChangeType.Modify; + } +} + +export function getChangeTypeColor(theme: IColorTheme, changeType: ChangeType): Color | undefined { + switch (changeType) { + case ChangeType.Modify: return theme.getColor(editorGutterModifiedBackground); + case ChangeType.Add: return theme.getColor(editorGutterAddedBackground); + case ChangeType.Delete: return theme.getColor(editorGutterDeletedBackground); + } +} + +export function compareChanges(a: IChange, b: IChange): number { + let result = a.modifiedStartLineNumber - b.modifiedStartLineNumber; + + if (result !== 0) { + return result; + } + + result = a.modifiedEndLineNumber - b.modifiedEndLineNumber; + + if (result !== 0) { + return result; + } + + result = a.originalStartLineNumber - b.originalStartLineNumber; + + if (result !== 0) { + return result; + } + + return a.originalEndLineNumber - b.originalEndLineNumber; +} + +export function getChangeHeight(change: IChange): number { + const modified = change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1; + const original = change.originalEndLineNumber - change.originalStartLineNumber + 1; + + if (change.originalEndLineNumber === 0) { + return modified; + } else if (change.modifiedEndLineNumber === 0) { + return original; + } else { + return modified + original; + } +} + +export function getModifiedEndLineNumber(change: IChange): number { + if (change.modifiedEndLineNumber === 0) { + return change.modifiedStartLineNumber === 0 ? 1 : change.modifiedStartLineNumber; + } else { + return change.modifiedEndLineNumber; + } +} + +export function lineIntersectsChange(lineNumber: number, change: IChange): boolean { + // deletion at the beginning of the file + if (lineNumber === 1 && change.modifiedStartLineNumber === 0 && change.modifiedEndLineNumber === 0) { + return true; + } + + return lineNumber >= change.modifiedStartLineNumber && lineNumber <= (change.modifiedEndLineNumber || change.modifiedStartLineNumber); +} diff --git a/src/vs/workbench/contrib/scm/common/quickDiffService.ts b/src/vs/workbench/contrib/scm/common/quickDiffService.ts index 38a9d67a555..5b872ae5722 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiffService.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiffService.ts @@ -80,3 +80,8 @@ export class QuickDiffService extends Disposable implements IQuickDiffService { return diffs.filter(this.isQuickDiff); } } + +export async function getOriginalResource(quickDiffService: IQuickDiffService, uri: URI, language: string | undefined, isSynchronized: boolean | undefined): Promise { + const quickDiffs = await quickDiffService.getQuickDiffs(uri, language, isSynchronized); + return quickDiffs.length > 0 ? quickDiffs[0].originalResource : null; +} From d66eb6b82f4fd73db17a4b7cc0799f90a09207bc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Dec 2024 11:37:50 +0100 Subject: [PATCH 046/479] fixes https://github.com/microsoft/vscode/issues/210254 (#235603) * fixes https://github.com/microsoft/vscode/issues/213148 * fixes https://github.com/microsoft/vscode/issues/210254 --- src/vs/base/common/filters.ts | 2 +- src/vs/editor/contrib/suggest/browser/suggestWidget.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 2ab6c40126d..aa0b036ac76 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -675,7 +675,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu } let diagScore = 0; - if (score !== Number.MAX_SAFE_INTEGER) { + if (score !== Number.MIN_SAFE_INTEGER) { canComeDiag = true; diagScore = score + _table[row - 1][column - 1]; } diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 7b728a6e171..048572143b1 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -928,7 +928,7 @@ export class SuggestWidget implements IDisposable { typicalHalfwidthCharacterWidth: fontInfo.typicalHalfwidthCharacterWidth, verticalPadding: 22, horizontalPadding: 14, - defaultSize: new dom.Dimension(430, statusBarHeight + 12 * itemHeight + borderHeight) + defaultSize: new dom.Dimension(430, statusBarHeight + 12 * itemHeight) }; } From 2299470205bc79ed6a35526384056ed6a90fee8c Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:19:51 +0100 Subject: [PATCH 047/479] Fix separator overlay issue in settings editor (#235608) fixes #203353 --- .../contrib/preferences/browser/media/settingsEditor2.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 18b04188fc6..aa1fb1c0536 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -151,6 +151,10 @@ margin-top: 14px; } +.settings-editor > .settings-body > .monaco-split-view2.separator-border .split-view-view:not(:first-child):before { + z-index: 16; /* Above sticky scroll */ +} + .settings-editor > .settings-body .settings-toc-container, .settings-editor > .settings-body .settings-tree-container { height: 100%; From abe1f677d0c149a6789c6464ed78c4b362f6b3c8 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:04:01 +0100 Subject: [PATCH 048/479] Fix focus handling during outline control changes (#234905) * fix setting focus when outline is changing control * :lipstik: --- .../workbench/contrib/outline/browser/outlinePane.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 6149cd6de17..aa34cef4e64 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -126,8 +126,10 @@ export class OutlinePane extends ViewPane implements IOutlinePane { } override focus(): void { - super.focus(); - this._tree?.domFocus(); + this._editorControlChangePromise.then(() => { + super.focus(); + this._tree?.domFocus(); + }); } protected override renderBody(container: HTMLElement): void { @@ -197,17 +199,18 @@ export class OutlinePane extends ViewPane implements IOutlinePane { return false; } + private _editorControlChangePromise: Promise = Promise.resolve(); private _handleEditorChanged(pane: IEditorPane | undefined): void { this._editorPaneDisposables.clear(); if (pane) { // react to control changes from within pane (https://github.com/microsoft/vscode/issues/134008) this._editorPaneDisposables.add(pane.onDidChangeControl(() => { - this._handleEditorControlChanged(pane); + this._editorControlChangePromise = this._handleEditorControlChanged(pane); })); } - this._handleEditorControlChanged(pane); + this._editorControlChangePromise = this._handleEditorControlChanged(pane); } private async _handleEditorControlChanged(pane: IEditorPane | undefined): Promise { From d5746c5593f6afe15e44a9f7877a064df6605d11 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:12:56 +0100 Subject: [PATCH 049/479] Fix inconsistent focus behavior when toggling views/panels (#235622) fix #232790 --- src/vs/workbench/browser/layout.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index eb8657fba82..782d0f48f38 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1754,9 +1754,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi else if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)) { const viewletToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Sidebar); if (viewletToOpen) { - const viewlet = this.paneCompositeService.openPaneComposite(viewletToOpen, ViewContainerLocation.Sidebar, true); + const viewlet = this.paneCompositeService.openPaneComposite(viewletToOpen, ViewContainerLocation.Sidebar); if (!viewlet) { - this.paneCompositeService.openPaneComposite(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id, ViewContainerLocation.Sidebar, true); + this.paneCompositeService.openPaneComposite(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id, ViewContainerLocation.Sidebar); } } } @@ -1895,8 +1895,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (panelToOpen) { - const focus = !skipLayout; - this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.Panel, focus); + this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.Panel); } } @@ -1995,8 +1994,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (panelToOpen) { - const focus = !skipLayout; - this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.AuxiliaryBar, focus); + this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.AuxiliaryBar); } } From 07a9dc6328ea308487036d555553bffe914735f2 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 9 Dec 2024 15:01:44 +0100 Subject: [PATCH 050/479] IPC transform error cause (#235632) --- src/vs/base/common/errors.ts | 9 ++++- src/vs/base/test/common/errors.test.ts | 51 ++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index fb18cb9b2e5..a6930ef51cf 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -126,20 +126,22 @@ export interface SerializedError { readonly message: string; readonly stack: string; readonly noTelemetry: boolean; + readonly cause?: SerializedError; } export function transformErrorForSerialization(error: Error): SerializedError; export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { if (error instanceof Error) { - const { name, message } = error; + const { name, message, cause } = error; const stack: string = (error).stacktrace || (error).stack; return { $isError: true, name, message, stack, - noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error) + noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error), + cause: cause ? transformErrorForSerialization(cause) : undefined }; } @@ -157,6 +159,9 @@ export function transformErrorFromSerialization(data: SerializedError): Error { } error.message = data.message; error.stack = data.stack; + if (data.cause) { + error.cause = transformErrorFromSerialization(data.cause); + } return error; } diff --git a/src/vs/base/test/common/errors.test.ts b/src/vs/base/test/common/errors.test.ts index 9e33ed81868..9dec9b4d26a 100644 --- a/src/vs/base/test/common/errors.test.ts +++ b/src/vs/base/test/common/errors.test.ts @@ -6,6 +6,8 @@ import assert from 'assert'; import { toErrorMessage } from '../../common/errorMessage.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; +import { transformErrorForSerialization, transformErrorFromSerialization } from '../../common/errors.js'; +import { assertType } from '../../common/types.js'; suite('Errors', () => { ensureNoDisposablesAreLeakedInTestSuite(); @@ -33,4 +35,53 @@ suite('Errors', () => { assert.ok(toErrorMessage(error, true).length > 'An unknown error occurred. Please consult the log for more details.'.length); } }); + + test('Transform Error for Serialization', function () { + const error = new Error('Test error'); + const serializedError = transformErrorForSerialization(error); + assert.strictEqual(serializedError.name, 'Error'); + assert.strictEqual(serializedError.message, 'Test error'); + assert.strictEqual(serializedError.stack, error.stack); + assert.strictEqual(serializedError.noTelemetry, false); + assert.strictEqual(serializedError.cause, undefined); + }); + + test('Transform Error with Cause for Serialization', function () { + const cause = new Error('Cause error'); + const error = new Error('Test error', { cause }); + const serializedError = transformErrorForSerialization(error); + assert.strictEqual(serializedError.name, 'Error'); + assert.strictEqual(serializedError.message, 'Test error'); + assert.strictEqual(serializedError.stack, error.stack); + assert.strictEqual(serializedError.noTelemetry, false); + assert.ok(serializedError.cause); + assert.strictEqual(serializedError.cause?.name, 'Error'); + assert.strictEqual(serializedError.cause?.message, 'Cause error'); + assert.strictEqual(serializedError.cause?.stack, cause.stack); + }); + + test('Transform Error from Serialization', function () { + const serializedError = transformErrorForSerialization(new Error('Test error')); + const error = transformErrorFromSerialization(serializedError); + assert.strictEqual(error.name, 'Error'); + assert.strictEqual(error.message, 'Test error'); + assert.strictEqual(error.stack, serializedError.stack); + assert.strictEqual(error.cause, undefined); + }); + + test('Transform Error with Cause from Serialization', function () { + const cause = new Error('Cause error'); + const serializedCause = transformErrorForSerialization(cause); + const error = new Error('Test error', { cause }); + const serializedError = transformErrorForSerialization(error); + const deserializedError = transformErrorFromSerialization(serializedError); + assert.strictEqual(deserializedError.name, 'Error'); + assert.strictEqual(deserializedError.message, 'Test error'); + assert.strictEqual(deserializedError.stack, serializedError.stack); + assert.ok(deserializedError.cause); + assertType(deserializedError.cause instanceof Error); + assert.strictEqual(deserializedError.cause?.name, 'Error'); + assert.strictEqual(deserializedError.cause?.message, 'Cause error'); + assert.strictEqual(deserializedError.cause?.stack, serializedCause.stack); + }); }); From 4cbf59e5dbeff7a177aae4e2a038ee9ee825e81e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Dec 2024 15:28:48 +0100 Subject: [PATCH 051/479] fix #235462 (#235636) --- src/vs/platform/userDataSync/common/extensionsSync.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 1715f8cbbe9..4054babf6f0 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -485,6 +485,7 @@ export class LocalExtensionsProvider { installGivenVersion: e.pinned && !!e.version, pinned: e.pinned, installPreReleaseVersion: e.preRelease, + preRelease: e.preRelease, profileLocation: profile.extensionsResource, isApplicationScoped: e.isApplicationScoped, context: { [EXTENSION_INSTALL_SKIP_WALKTHROUGH_CONTEXT]: true, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.SETTINGS_SYNC } From f3a02f3d14117b7db8824237f71da3c61908e25a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Dec 2024 15:30:25 +0100 Subject: [PATCH 052/479] chat - indicate quota hit in `prominent` status (#235637) --- .../parts/statusbar/media/statusbarpart.css | 12 +++- .../browser/parts/statusbar/statusbarPart.ts | 70 +++++++++++++++++-- .../contrib/chat/browser/chatQuotasService.ts | 25 ++++--- .../services/statusbar/browser/statusbar.ts | 12 ++++ 4 files changed, 101 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css index b30d2bee088..b7b1bd7dd7c 100644 --- a/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css +++ b/src/vs/workbench/browser/parts/statusbar/media/statusbarpart.css @@ -205,7 +205,17 @@ background-color: var(--vscode-statusBarItem-prominentBackground); } -.monaco-workbench .part.statusbar > .items-container > .statusbar-item.prominent-kind a:hover:not(.disabled) { +/** + * Using :not(.compact-right):not(.compact-left) here to improve the visual appearance + * when a prominent item uses `compact: true` with other items. The presence of the + * !important directive for `background-color` otherwise blocks our special hover handling + * code here: + * https://github.com/microsoft/vscode/blob/c2037f152b2bb3119ce1d87f52987776540dd57f/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts#L483-L505 + * + * Note: this is currently only done for the prominent kind, but needs to be expanded if + * other kinds use compact feature. + */ +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.prominent-kind:not(.compact-right):not(.compact-left) a:hover:not(.disabled) { color: var(--vscode-statusBarItem-prominentHoverForeground); background-color: var(--vscode-statusBarItem-prominentHoverBackground) !important; } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index fddbd06e307..7454d382d0c 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -5,11 +5,11 @@ import './media/statusbarpart.css'; import { localize } from '../../../../nls.js'; -import { Disposable, DisposableStore, dispose, disposeIfDisposable, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, disposeIfDisposable, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { MultiWindowParts, Part } from '../../part.js'; import { EventType as TouchEventType, Gesture, GestureEvent } from '../../../../base/browser/touch.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarStyleOverride, isStatusbarEntryLocation, IStatusbarEntryLocation, isStatusbarEntryPriority, IStatusbarEntryPriority } from '../../../services/statusbar/browser/statusbar.js'; +import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarStyleOverride, isStatusbarEntryLocation, IStatusbarEntryLocation, isStatusbarEntryPriority, IStatusbarEntryPriority, IStatusbarEntryOverride } from '../../../services/statusbar/browser/statusbar.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IAction, Separator, toAction } from '../../../../base/common/actions.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; @@ -75,6 +75,12 @@ export interface IStatusbarEntryContainer extends IDisposable { */ updateEntryVisibility(id: string, visible: boolean): void; + /** + * Allows to override the appearance of an entry with the provided ID. Only a subset + * of properties is allowed to be overridden. + */ + overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable; + /** * Focused the status bar. If one of the status bar entries was focused, focuses it directly. */ @@ -134,6 +140,9 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { private readonly _onWillDispose = this._register(new Emitter()); readonly onWillDispose = this._onWillDispose.event; + private readonly onDidOverrideEntry = this._register(new Emitter()); + private readonly entryOverrides = new Map(); + private leftItemsContainer: HTMLElement | undefined; private rightItemsContainer: HTMLElement | undefined; @@ -173,6 +182,28 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles())); } + overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable { + this.entryOverrides.set(id, override); + this.onDidOverrideEntry.fire(id); + + return toDisposable(() => { + const currentOverride = this.entryOverrides.get(id); + if (currentOverride === override) { + this.entryOverrides.delete(id); + this.onDidOverrideEntry.fire(id); + } + }); + } + + private withEntryOverride(entry: IStatusbarEntry, id: string): IStatusbarEntry { + const override = this.entryOverrides.get(id); + if (override) { + entry = { ...entry, ...override }; + } + + return entry; + } + addEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priorityOrLocation: number | IStatusbarEntryLocation | IStatusbarEntryPriority = 0): IStatusbarEntryAccessor { let priority: IStatusbarEntryPriority; if (isStatusbarEntryPriority(priorityOrLocation)) { @@ -220,10 +251,11 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { } private doAddEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priority: IStatusbarEntryPriority): IStatusbarEntryAccessor { + const disposables = new DisposableStore(); // View model item const itemContainer = this.doCreateStatusItem(id, alignment); - const item = this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, entry, this.hoverDelegate); + const item = disposables.add(this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, this.withEntryOverride(entry, id), this.hoverDelegate)); // View model entry const viewModelEntry: IStatusbarViewModelEntry = new class implements IStatusbarViewModelEntry { @@ -245,9 +277,11 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { this.appendStatusbarEntry(viewModelEntry); } - return { + let lastEntry = entry; + const accessor: IStatusbarEntryAccessor = { update: entry => { - item.update(entry); + lastEntry = entry; + item.update(this.withEntryOverride(entry, id)); }, dispose: () => { const { needsFullRefresh } = this.doAddOrRemoveModelEntry(viewModelEntry, false); @@ -255,10 +289,20 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { this.appendStatusbarEntries(); } else { itemContainer.remove(); + this.updateCompactEntries(); } - dispose(item); + disposables.dispose(); } }; + + // React to overrides + disposables.add(this.onDidOverrideEntry.event(overrideEntryId => { + if (overrideEntryId === id) { + accessor.update(lastEntry); + } + })); + + return accessor; } private doCreateStatusItem(id: string, alignment: StatusbarAlignment, ...extraClasses: string[]): HTMLElement { @@ -790,6 +834,16 @@ export class StatusbarService extends MultiWindowParts implements } } + overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable { + const disposables = new DisposableStore(); + + for (const part of this.parts) { + disposables.add(part.overrideEntry(id, override)); + } + + return disposables; + } + focus(preserveEntryFocus?: boolean): void { this.activePart.focus(preserveEntryFocus); } @@ -856,6 +910,10 @@ export class ScopedStatusbarService extends Disposable implements IStatusbarServ this.statusbarEntryContainer.updateEntryVisibility(id, visible); } + overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable { + return this.statusbarEntryContainer.overrideEntry(id, override); + } + focus(preserveEntryFocus?: boolean): void { this.statusbarEntryContainer.focus(preserveEntryFocus); } diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index 467affc7899..7b82ddbb330 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -8,7 +8,7 @@ import { safeIntl } from '../../../../base/common/date.js'; import { language } from '../../../../base/common/platform.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; -import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; @@ -18,7 +18,7 @@ import { createDecorator, ServicesAccessor } from '../../../../platform/instanti import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import product from '../../../../platform/product/common/product.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; +import { IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; @@ -212,7 +212,7 @@ export class ChatQuotasStatusBarEntry extends Disposable implements IWorkbenchCo private static readonly COPILOT_STATUS_ID = 'GitHub.copilot.status'; // TODO@bpasero unify into 1 core indicator - private readonly _entry = this._register(new MutableDisposable()); + private readonly entry = this._register(new DisposableStore()); constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, @@ -221,12 +221,17 @@ export class ChatQuotasStatusBarEntry extends Disposable implements IWorkbenchCo super(); this._register(Event.runAndSubscribe(this.chatQuotasService.onDidChangeQuotas, () => this.updateStatusbarEntry())); + this._register(this.statusbarService.onDidChangeEntryVisibility(e => { + if (e.id === ChatQuotasStatusBarEntry.COPILOT_STATUS_ID) { + this.updateStatusbarEntry(); + } + })); } private updateStatusbarEntry(): void { - const { chatQuotaExceeded, completionsQuotaExceeded } = this.chatQuotasService.quotas; + this.entry.clear(); - // Some quota exceeded, show indicator + const { chatQuotaExceeded, completionsQuotaExceeded } = this.chatQuotasService.quotas; if (chatQuotaExceeded || completionsQuotaExceeded) { let text: string; if (chatQuotaExceeded && !completionsQuotaExceeded) { @@ -242,23 +247,21 @@ export class ChatQuotasStatusBarEntry extends Disposable implements IWorkbenchCo text = `$(copilot-warning) ${text}`; } - this._entry.value = this.statusbarService.addEntry({ + this.entry.add(this.statusbarService.addEntry({ name: localize('indicator', "Copilot Limit Indicator"), text, ariaLabel: text, command: OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, showInAllWindows: true, + kind: 'prominent', tooltip: quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }) }, ChatQuotasStatusBarEntry.ID, StatusbarAlignment.RIGHT, { id: ChatQuotasStatusBarEntry.COPILOT_STATUS_ID, alignment: StatusbarAlignment.RIGHT, compact: isCopilotStatusVisible - }); - } + })); - // No quota exceeded, remove indicator - else { - this._entry.clear(); + this.entry.add(this.statusbarService.overrideEntry(ChatQuotasStatusBarEntry.COPILOT_STATUS_ID, { kind: 'prominent' })); } } } diff --git a/src/vs/workbench/services/statusbar/browser/statusbar.ts b/src/vs/workbench/services/statusbar/browser/statusbar.ts index 3b3f1f1fe17..1c589dc4f66 100644 --- a/src/vs/workbench/services/statusbar/browser/statusbar.ts +++ b/src/vs/workbench/services/statusbar/browser/statusbar.ts @@ -197,3 +197,15 @@ export interface IStatusbarEntryAccessor extends IDisposable { */ update(properties: IStatusbarEntry): void; } + +/** + * A way to override a status bar entry appearance. Only a subset of + * properties are currently allowed to override. + */ +export interface IStatusbarEntryOverride { + + /** + * The kind of status bar entry. This applies different colors to the entry. + */ + readonly kind?: StatusbarEntryKind; +} From 5fff871b240a64708aa5083b36907fc859049df1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 9 Dec 2024 15:47:47 +0100 Subject: [PATCH 053/479] fix #233160 (#235638) --- src/vs/platform/configuration/common/configuration.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index a93872f4cd7..e9a9143fa22 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -258,6 +258,10 @@ export function removeFromValueTree(valueTree: any, key: string): void { } function doRemoveFromValueTree(valueTree: any, segments: string[]): void { + if (!valueTree) { + return; + } + const first = segments.shift()!; if (segments.length === 0) { // Reached last segment From 869a6422160a8716f09ae54f3ba6a767dc30bb05 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 9 Dec 2024 17:23:26 +0100 Subject: [PATCH 054/479] fix falsy error (#235651) --- src/vs/editor/common/languages/defaultDocumentColorsComputer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts b/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts index 3c8acf2c7cd..74bb0757905 100644 --- a/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts +++ b/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts @@ -36,7 +36,7 @@ function _toIColor(r: number, g: number, b: number, a: number): IColor { function _findRange(model: IDocumentColorComputerTarget, match: RegExpMatchArray): IRange | undefined { const index = match.index; const length = match[0].length; - if (!index) { + if (index === undefined) { return; } const startPosition = model.positionAt(index); From 686c08b1529ba857b3dcd3cb8de298efca43454e Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:01:01 +0100 Subject: [PATCH 055/479] Fix focus outline cropping issue in tabs (#235655) fix #227552 --- .../browser/parts/editor/media/multieditortabscontrol.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css index 93559402aa5..d0bb1d3d186 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -50,7 +50,7 @@ display: none; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.tabs-border-bottom::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tab:not([tabIndex="0"]) .tabs-and-actions-container.tabs-border-bottom::after { content: ''; position: absolute; bottom: 0; @@ -293,7 +293,7 @@ background-color: var(--tab-border-top-color); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom:not([tabIndex="0"]) > .tab-border-bottom-container { z-index: 10; bottom: 0; height: 1px; From d0507ca130176a0e89f56858bbee058bf7f2daf0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 9 Dec 2024 18:20:38 +0100 Subject: [PATCH 056/479] add logging for non-existing chat session restoration (#235661) --- .../browser/chatEditing/chatEditingService.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts index 458687df09a..912b37c6624 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts @@ -22,6 +22,7 @@ import { localize, localize2 } from '../../../../../nls.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ILogService } from '../../../../../platform/log/common/log.js'; import { bindContextKey } from '../../../../../platform/observable/common/platformObservableUtils.js'; import { IProgressService, ProgressLocation } from '../../../../../platform/progress/common/progress.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; @@ -89,6 +90,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic @ILifecycleService private readonly lifecycleService: ILifecycleService, @IWorkbenchAssignmentService private readonly _workbenchAssignmentService: IWorkbenchAssignmentService, @IStorageService storageService: IStorageService, + @ILogService logService: ILogService, ) { super(); this._applyingChatEditsFailedContextKey = applyingChatEditsFailedContextKey.bindTo(contextKeyService); @@ -157,11 +159,16 @@ export class ChatEditingService extends Disposable implements IChatEditingServic void this._editingSessionFileLimitPromise; const sessionIdToRestore = storageService.get(STORAGE_KEY_EDITING_SESSION, StorageScope.WORKSPACE); - if (isString(sessionIdToRestore) && this._chatService.getOrRestoreSession(sessionIdToRestore)) { - this._restoringEditingSession = this.startOrContinueEditingSession(sessionIdToRestore); - this._restoringEditingSession.finally(() => { - this._restoringEditingSession = undefined; - }); + if (isString(sessionIdToRestore)) { + if (this._chatService.getOrRestoreSession(sessionIdToRestore)) { + this._restoringEditingSession = this.startOrContinueEditingSession(sessionIdToRestore); + this._restoringEditingSession.finally(() => { + this._restoringEditingSession = undefined; + }); + } else { + logService.error(`Edit session session to restore is a non-existing chat session: ${sessionIdToRestore}`); + } + storageService.remove(STORAGE_KEY_EDITING_SESSION, StorageScope.WORKSPACE); } } From 8a0ad0168e90a6715905943a04fdabc8cf7f423c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 9 Dec 2024 18:20:43 +0100 Subject: [PATCH 057/479] [themes] 'Generate Color Scheme From Current Settings' produces deprecated settings --- src/vs/workbench/contrib/themes/browser/themes.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 9a7b33449cf..8549b93b80c 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -661,7 +661,7 @@ registerAction2(class extends Action2 { const theme = themeService.getColorTheme(); const colors = Registry.as(ColorRegistryExtensions.ColorContribution).getColors(); - const colorIds = colors.map(c => c.id).sort(); + const colorIds = colors.filter(c => !c.deprecationMessage).map(c => c.id).sort(); const resultingColors: { [key: string]: string | null } = {}; const inherited: string[] = []; for (const colorId of colorIds) { From 94d106df7f5577ef27b5c40bee0a3c3fd22d9b04 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 9 Dec 2024 21:04:56 +0100 Subject: [PATCH 058/479] chat - disable cache for requests (#235673) --- src/vs/base/parts/request/common/request.ts | 5 +++++ src/vs/base/parts/request/common/requestImpl.ts | 10 +++++++--- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vs/base/parts/request/common/request.ts b/src/vs/base/parts/request/common/request.ts index 773021faacb..b9e03ca4d7f 100644 --- a/src/vs/base/parts/request/common/request.ts +++ b/src/vs/base/parts/request/common/request.ts @@ -45,6 +45,11 @@ export interface IRequestOptions { data?: string; followRedirects?: number; proxyAuthorization?: string; + /** + * A signal to not cache the response. This may not + * be supported in all implementations. + */ + disableCache?: boolean; } export interface IRequestContext { diff --git a/src/vs/base/parts/request/common/requestImpl.ts b/src/vs/base/parts/request/common/requestImpl.ts index 11aac6bd6f0..17f80575ef5 100644 --- a/src/vs/base/parts/request/common/requestImpl.ts +++ b/src/vs/base/parts/request/common/requestImpl.ts @@ -21,12 +21,16 @@ export async function request(options: IRequestOptions, token: CancellationToken ]) : cancellation.signal; try { - const res = await fetch(options.url || '', { + const fetchInit: RequestInit = { method: options.type || 'GET', headers: getRequestHeaders(options), body: options.data, - signal, - }); + signal + }; + if (options.disableCache) { + fetchInit.cache = 'no-store'; + } + const res = await fetch(options.url || '', fetchInit); return { res: { statusCode: res.status, diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 485d1489dfd..3e43f4428ec 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -541,6 +541,7 @@ class ChatSetupRequests extends Disposable { type, url, data: type === 'POST' ? JSON.stringify(body) : undefined, + disableCache: true, headers: { 'Authorization': `Bearer ${session.accessToken}` } From 4170a3f55ac9682b9ba7584450f4d6a86c4a2fac Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 9 Dec 2024 15:13:56 -0500 Subject: [PATCH 059/479] fix terminal suggest folder/file completions bugs (#235165) --- .../browser/terminalCompletionService.ts | 38 +++--- .../browser/terminalCompletionService.test.ts | 117 ++++++++++-------- .../test/common/workbenchTestServices.ts | 8 +- 3 files changed, 93 insertions(+), 70 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts index 19ac7672bd9..ea3b6cf16bf 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts @@ -5,6 +5,7 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Disposable, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../../base/common/network.js'; +import { basename } from '../../../../../base/common/path.js'; import { URI } from '../../../../../base/common/uri.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; @@ -208,13 +209,9 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo } const resourceCompletions: ITerminalCompletion[] = []; - const endsWithSpace = promptValue.substring(0, cursorPosition).endsWith(' '); - let lastWord; - if (endsWithSpace) { - lastWord = ''; - } else { - lastWord = promptValue.substring(0, cursorPosition).trim().split(' ').at(-1) ?? ''; - } + const cursorPrefix = promptValue.substring(0, cursorPosition); + const endsWithSpace = cursorPrefix.endsWith(' '); + const lastWord = endsWithSpace ? '' : cursorPrefix.split(' ').at(-1) ?? ''; for (const stat of fileStat.children) { let kind: TerminalCompletionItemKind | undefined; @@ -228,26 +225,35 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo continue; } const isDirectory = kind === TerminalCompletionItemKind.Folder; - const pathToResource = stat.resource.fsPath.replace(cwd.fsPath, ''); - let label = pathToResource; + const fileName = basename(stat.resource.fsPath); + + let label; + if (!lastWord.startsWith('.' + resourceRequestConfig.pathSeparator) && !lastWord.startsWith('..' + resourceRequestConfig.pathSeparator)) { + // add a dot to the beginning of the label if it doesn't already have one + label = `.${resourceRequestConfig.pathSeparator}${fileName}`; + } else { + if (lastWord.endsWith(resourceRequestConfig.pathSeparator)) { + label = `${lastWord}${fileName}`; + } else { + label = `${lastWord}${resourceRequestConfig.pathSeparator}${fileName}`; + } + if (lastWord.length && lastWord.at(-1) !== resourceRequestConfig.pathSeparator && lastWord.at(-1) !== '.') { + label = `.${resourceRequestConfig.pathSeparator}${fileName}`; + } + } if (isDirectory && !label.endsWith(resourceRequestConfig.pathSeparator)) { label = label + resourceRequestConfig.pathSeparator; } - const startsWithDot = lastWord && lastWord.startsWith('.'); - if (!startsWithDot) { - label = '.' + label; - } resourceCompletions.push({ label, kind, isDirectory, isFile: kind === TerminalCompletionItemKind.File, - replacementIndex: promptValue[cursorPosition - 1] === ' ' ? cursorPosition : cursorPosition - 1, - replacementLength: label.length - lastWord.length > 0 ? label.length - lastWord.length : label.length, + replacementIndex: cursorPosition - lastWord.length > 0 ? cursorPosition - lastWord.length : cursorPosition, + replacementLength: lastWord.length > 0 ? lastWord.length : label.length }); } return resourceCompletions.length ? resourceCompletions : undefined; } } - diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts index d9e217ee398..b3611df7444 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts @@ -4,45 +4,44 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; -import { IFileStat, IFileStatWithMetadata, IResolveFileOptions, IResolveMetadataFileOptions } from '../../../../../../platform/files/common/files.js'; +import { IFileService, IFileStatWithMetadata, IResolveMetadataFileOptions } from '../../../../../../platform/files/common/files.js'; import { TerminalCompletionService, TerminalCompletionItemKind, TerminalResourceRequestConfig } from '../../browser/terminalCompletionService.js'; -import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import sinon from 'sinon'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; -import { TestFileService } from '../../../../../test/browser/workbenchTestServices.js'; -import { TestConfigurationService } from '../../../../../../platform/configuration/test/common/testConfigurationService.js'; import assert from 'assert'; import { isWindows } from '../../../../../../base/common/platform.js'; - -class TestFileService2 extends TestFileService { - private _children: IFileStat[] = []; - constructor() { - super(); - } - setChildren(children: IFileStat[]) { - this._children = children; - } - override async resolve(resource: URI, _options: IResolveMetadataFileOptions): Promise; - override async resolve(resource: URI, _options?: IResolveFileOptions): Promise; - override async resolve(resource: URI, _options?: IResolveFileOptions): Promise { - const result = await super.resolve(resource, _options); - result.children = this._children; - return result; - } -} +import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { createFileStat } from '../../../../../test/common/workbenchTestServices.js'; +import { TestConfigurationService } from '../../../../../../platform/configuration/test/common/testConfigurationService.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; suite('TerminalCompletionService', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; + let configurationService: TestConfigurationService; + let validResources: URI[]; + let childResources: { resource: URI; isFile?: boolean; isDirectory?: boolean }[]; const pathSeparator = isWindows ? '\\' : '/'; - let fileService: TestFileService2; - let configurationService: IConfigurationService; let terminalCompletionService: TerminalCompletionService; - const store = ensureNoDisposablesAreLeakedInTestSuite(); + setup(() => { - fileService = new TestFileService2(); + instantiationService = store.add(new TestInstantiationService()); configurationService = new TestConfigurationService(); - terminalCompletionService = new TerminalCompletionService(configurationService, fileService); - store.add(terminalCompletionService); - store.add(fileService); + instantiationService.stub(IConfigurationService, configurationService); + instantiationService.stub(IFileService, { + async stat(resource) { + if (!validResources.map(e => e.path).includes(resource.path)) { + throw new Error('Doesn\'t exist'); + } + return createFileStat(resource); + }, + async resolve(resource: URI, options: IResolveMetadataFileOptions): Promise { + return createFileStat(resource, undefined, undefined, undefined, childResources); + } + }); + terminalCompletionService = store.add(instantiationService.createInstance(TerminalCompletionService)); + validResources = []; + childResources = []; }); teardown(() => { @@ -63,6 +62,7 @@ suite('TerminalCompletionService', () => { cwd: URI.parse('file:///test'), pathSeparator }; + validResources = [URI.parse('file:///test')]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ', 3); assert(!result); }); @@ -75,21 +75,20 @@ suite('TerminalCompletionService', () => { foldersRequested: true, pathSeparator }; - await fileService.createFolder(URI.parse('file:///test/')); - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false, isSymbolicLink: false, mtime: 0, size: 0, children: [] }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true, isSymbolicLink: true, mtime: 0, size: 0, children: [] }; - fileService.setChildren([childFolder, childFile]); + validResources = [URI.parse('file:///test')]; + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ', 3); assert(!!result); assert(result.length === 1); - const label = `.${pathSeparator}folder1${pathSeparator}`; assert.deepEqual(result![0], { - label, + label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 3, - replacementLength: label.length + replacementLength: 10 }); }); test('cd .', async () => { @@ -98,21 +97,20 @@ suite('TerminalCompletionService', () => { foldersRequested: true, pathSeparator }; - await fileService.createFolder(URI.parse('file:///test/')); - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false, isSymbolicLink: false, mtime: 0, size: 0, children: [] }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true, isSymbolicLink: true, mtime: 0, size: 0, children: [] }; - fileService.setChildren([childFolder, childFile]); + validResources = [URI.parse('file:///test/')]; + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd .', 4); assert(!!result); assert(result.length === 1); - const label = `${pathSeparator}folder1${pathSeparator}`; assert.deepEqual(result![0], { - label, + label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 3, - replacementLength: label.length - 1 + replacementLength: 1 // replacing . }); }); test('cd ./', async () => { @@ -121,21 +119,40 @@ suite('TerminalCompletionService', () => { foldersRequested: true, pathSeparator }; - await fileService.createFolder(URI.parse('file:///test/')); - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false, isSymbolicLink: false, mtime: 0, size: 0, children: [] }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true, isSymbolicLink: true, mtime: 0, size: 0, children: [] }; - fileService.setChildren([childFolder, childFile]); + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ./', 5); assert(!!result); assert(result.length === 1); - const label = `${pathSeparator}folder1${pathSeparator}`; assert.deepEqual(result![0], { - label, + label: `.${pathSeparator}folder1${pathSeparator}`, + kind: TerminalCompletionItemKind.Folder, + isDirectory: true, + isFile: false, + replacementIndex: 3, + replacementLength: 2 // replacing ./ + }); + }); + test('cd ./f', async () => { + const resourceRequestConfig: TerminalResourceRequestConfig = { + cwd: URI.parse('file:///test'), + foldersRequested: true, + pathSeparator + }; + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; + const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ./f', 6); + assert(!!result); + assert(result.length === 1); + assert.deepEqual(result![0], { + label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, - replacementIndex: 4, - replacementLength: label.length - 2 + replacementIndex: 3, + replacementLength: 3 // replacing ./f }); }); }); diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index cd6e844b2cb..ef7fce72b1e 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -225,20 +225,20 @@ export class TestWorkingCopy extends Disposable implements IWorkingCopy { } } -export function createFileStat(resource: URI, readonly = false): IFileStatWithMetadata { +export function createFileStat(resource: URI, readonly = false, isFile?: boolean, isDirectory?: boolean, children?: { resource: URI; isFile?: boolean; isDirectory?: boolean }[] | undefined): IFileStatWithMetadata { return { resource, etag: Date.now().toString(), mtime: Date.now(), ctime: Date.now(), size: 42, - isFile: true, - isDirectory: false, + isFile: isFile ?? true, + isDirectory: isDirectory ?? false, isSymbolicLink: false, readonly, locked: false, name: basename(resource), - children: undefined + children: children?.map(c => createFileStat(c.resource, false, c.isFile, c.isDirectory)) }; } From 8e1f20cbead806bf3e77908fbaf20ada2cc2d982 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Dec 2024 21:54:30 +0100 Subject: [PATCH 060/479] Workspace trust - remove unused code (#235676) --- .../src/extensionsProposals.ts | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/extensions/configuration-editing/src/extensionsProposals.ts b/extensions/configuration-editing/src/extensionsProposals.ts index 6438281dc0b..ba1c33e7845 100644 --- a/extensions/configuration-editing/src/extensionsProposals.ts +++ b/extensions/configuration-editing/src/extensionsProposals.ts @@ -30,29 +30,3 @@ export async function provideInstalledExtensionProposals(existing: string[], add } return []; } - -export async function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): Promise { - if (Array.isArray(existing)) { - const extensions = vscode.extensions.all.filter(e => e.packageJSON.main); - const extensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1); - if (extensionProposals.length) { - return extensionProposals.map(e => { - const item = new vscode.CompletionItem(e.id); - const insertText = `"${e.id}": {\n\t"supported": false,\n\t"version": "${e.packageJSON.version}"\n}`; - item.kind = vscode.CompletionItemKind.Value; - item.insertText = insertText; - item.range = range; - item.filterText = insertText; - return item; - }); - } else { - const example = new vscode.CompletionItem(vscode.l10n.t("Example")); - example.insertText = '"vscode.csharp: {\n\t"supported": false,\n\t"version": "0.0.0"\n}`;"'; - example.kind = vscode.CompletionItemKind.Value; - example.range = range; - return [example]; - } - } - - return []; -} From d0e9b3a84e4e2cb1ab0c7cc1c90acf75097d4f82 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Mon, 9 Dec 2024 13:59:32 -0800 Subject: [PATCH 061/479] update wording, add svg (#235668) * update wording add placeholder svg * small tweaks * moar tweaks * fix typo * match casing * Theme multi-file-edits * remove from multi-file-edits.svg * update multi-file-edits theme for improved consistency * update multi-file-edits.svg to use editor widget background color --------- Co-authored-by: bhavyaus --- .../common/gettingStartedContent.ts | 11 +- .../common/media/multi-file-edits.svg | 513 ++++++++++++++++++ 2 files changed, 518 insertions(+), 6 deletions(-) create mode 100644 src/vs/workbench/contrib/welcomeGettingStarted/common/media/multi-file-edits.svg diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 02350e77258..50e0e26bab2 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -211,10 +211,10 @@ export const startEntries: GettingStartedStartEntryContent = [ const Button = (title: string, href: string) => `[${title}](${href})`; const CopilotStepTitle = localize('gettingStarted.copilotSetup.title', "Use AI features with Copilot for free"); -const CopilotDescription = localize({ key: 'gettingStarted.copilotSetup.description', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "Write code faster and smarter with [Copilot]({0}) for free with your GitHub account.", product.defaultChatAgent?.documentationUrl ?? ''); +const CopilotDescription = localize({ key: 'gettingStarted.copilotSetup.description', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "Write code faster and smarter using [Copilot]({0}). With your GitHub account, get 2,000 code completions and 50 chat messages per month for free.", product.defaultChatAgent?.documentationUrl ?? ''); const CopilotTermsString = localize({ key: 'copilotTerms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to Copilot [Terms]({0}) and [Privacy Policy]({1}).", product.defaultChatAgent?.termsStatementUrl ?? '', product.defaultChatAgent?.privacyStatementUrl ?? ''); -const CopilotSignedOutButton = Button(localize('setupCopilotButton.signIn', "Sign in to use Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); -const CopilotSignedInButton = Button(localize('setupCopilotButton.setup', "Setup Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); +const CopilotSignedOutButton = Button(localize('setupCopilotButton.signIn', "Sign in to Use Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); +const CopilotSignedInButton = Button(localize('setupCopilotButton.setup', "Set Up Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); const CopilotCompleteButton = Button(localize('setupCopilotButton.chatWithCopilot', "Chat with Copilot"), 'command:workbench.action.chat.open'); function createCopilotSetupStep(id: string, button: string, when: string, includeTerms: boolean): BuiltinGettingStartedStep { @@ -228,9 +228,8 @@ function createCopilotSetupStep(id: string, button: string, when: string, includ description, when, media: { - type: 'markdown', - path: 'empty' - } + type: 'svg', altText: 'VS Code Copilot multi file edits', path: 'multi-file-edits.svg' + }, }; } 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 new file mode 100644 index 00000000000..e151a4b95f2 --- /dev/null +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/media/multi-file-edits.svg @@ -0,0 +1,513 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 853cbfe09344e0510d497ef5fbef68afc4650baa Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:43:54 -0800 Subject: [PATCH 062/479] Adjust notebooks (#235682) --- .vscode/notebooks/grooming.github-issues | 16 ++++++++-------- .vscode/notebooks/my-endgame.github-issues | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 8bb9a246d5f..cca871afdb8 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -7,22 +7,22 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\r\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\r" + "value": "// list of repos we work in\r\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\r\n" }, { "kind": 1, "language": "markdown", - "value": "#### Missing Type label\r" + "value": "#### Missing Type label\r\n" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r" + "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r\n" }, { "kind": 1, "language": "markdown", - "value": "#### Missing Area Label\r\n\r\nFeature area labels are light or strong blue (`1d76db` or `c5def5`) and they denote a specific feature or feature area, like `editor-clipboard` or `file-explorer`\r" + "value": "#### Missing Area Label\r\n\r\nFeature area labels are light or strong blue (`1d76db` or `c5def5`) and they denote a specific feature or feature area, like `editor-clipboard` or `file-explorer`\r\n" }, { "kind": 2, @@ -32,21 +32,21 @@ { "kind": 1, "language": "markdown", - "value": "### Missing Milestone\r" + "value": "### Missing Milestone\r\n" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed\r" + "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed -label:confirmation-pending\r\n" }, { "kind": 1, "language": "markdown", - "value": "#### Not Actionable\r" + "value": "#### Not Actionable\r\n" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:\"info-needed\"\r" + "value": "$repos assignee:@me is:open label:\"info-needed\"\r\n" } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 9d101de768b..b0be17265cd 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$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$MILESTONE=milestone:\"November 2024\"\n\n$MINE=assignee:@me" + "value": "$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\r\n\r\n$MILESTONE=milestone:\"November 2024\"\r\n\r\n$MINE=assignee:@me" }, { "kind": 1, @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:*out-of-scope -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen -author:benibenj -author:luabud -author:anthonykim1 -author:joshspicer -author:osortega -author:legomushroom" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:*out-of-scope -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:ntrogh -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen -author:benibenj -author:luabud -author:anthonykim1 -author:joshspicer -author:osortega -author:legomushroom" }, { "kind": 1, @@ -187,6 +187,6 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\r\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\r\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" } ] \ No newline at end of file From 05fd1308240072efb09749954fd478221fd01d55 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:35:26 -0800 Subject: [PATCH 063/479] Remove under-discussion from needing milestone (#235687) --- .vscode/notebooks/grooming.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index cca871afdb8..6e2e81b21a5 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -37,7 +37,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed -label:confirmation-pending\r\n" + "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed -label:confirmation-pending -label:under-discussion\r\n" }, { "kind": 1, From ff31a135b1901c4ca7e486ad25ef03c35aeca56c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Dec 2024 10:23:59 +0100 Subject: [PATCH 064/479] debt - implement `disableCache` for node/electron requests (#235709) --- src/vs/platform/request/node/requestService.ts | 6 +++++- .../electron-sandbox/emergencyAlert.contribution.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index 149d42059e1..38a582b044e 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -155,7 +155,7 @@ export async function nodeRequest(options: NodeRequestOptions, token: Cancellati ? options.getRawRequest(options) : await getNodeRequest(options); - const opts: https.RequestOptions = { + const opts: https.RequestOptions & { cache?: 'default' | 'no-store' | 'reload' | 'no-cache' | 'force-cache' | 'only-if-cached' } = { hostname: endpoint.hostname, port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), protocol: endpoint.protocol, @@ -170,6 +170,10 @@ export async function nodeRequest(options: NodeRequestOptions, token: Cancellati opts.auth = options.user + ':' + options.password; } + if (options.disableCache) { + opts.cache = 'no-store'; + } + const req = rawRequest(opts, (res: http.IncomingMessage) => { const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { diff --git a/src/vs/workbench/contrib/emergencyAlert/electron-sandbox/emergencyAlert.contribution.ts b/src/vs/workbench/contrib/emergencyAlert/electron-sandbox/emergencyAlert.contribution.ts index 6a9851fa5b9..6ef9584b413 100644 --- a/src/vs/workbench/contrib/emergencyAlert/electron-sandbox/emergencyAlert.contribution.ts +++ b/src/vs/workbench/contrib/emergencyAlert/electron-sandbox/emergencyAlert.contribution.ts @@ -58,7 +58,7 @@ export class EmergencyAlert implements IWorkbenchContribution { } private async doFetchAlerts(url: string): Promise { - const requestResult = await this.requestService.request({ type: 'GET', url }, CancellationToken.None); + const requestResult = await this.requestService.request({ type: 'GET', url, disableCache: true }, CancellationToken.None); if (requestResult.res.statusCode !== 200) { throw new Error(`Failed to fetch emergency alerts: HTTP ${requestResult.res.statusCode}`); From 7ccf9661c70637e3056795f6c00250e5def2a349 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:00:46 +0100 Subject: [PATCH 065/479] Git - keybindings to match command pallette `when` clause (#235713) --- 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 5c48bd5dc8b..5e7caad22bb 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -943,19 +943,19 @@ "command": "git.stageSelectedRanges", "key": "ctrl+k ctrl+alt+s", "mac": "cmd+k cmd+alt+s", - "when": "isInDiffEditor" + "when": "resourceScheme =~ /^git$|^file$/" }, { "command": "git.unstageSelectedRanges", "key": "ctrl+k ctrl+n", "mac": "cmd+k cmd+n", - "when": "isInDiffEditor" + "when": "isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.revertSelectedRanges", "key": "ctrl+k ctrl+r", "mac": "cmd+k cmd+r", - "when": "isInDiffEditor" + "when": "resourceScheme =~ /^git$|^file$/" } ], "menus": { From 21c52a4a3ae29fac345c863b39c5fd44f4005862 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 10 Dec 2024 11:02:51 +0100 Subject: [PATCH 066/479] onEnter Indentation Rules: Ignoring double slashes inside of strings (#235712) ignoring double slashes inside of strings --- extensions/cpp/language-configuration.json | 2 +- extensions/csharp/language-configuration.json | 2 +- extensions/go/language-configuration.json | 2 +- extensions/groovy/language-configuration.json | 2 +- extensions/java/language-configuration.json | 2 +- extensions/javascript/javascript-language-configuration.json | 2 +- extensions/json/language-configuration.json | 2 +- extensions/less/language-configuration.json | 2 +- extensions/objective-c/language-configuration.json | 2 +- extensions/php/language-configuration.json | 2 +- extensions/rust/language-configuration.json | 2 +- extensions/swift/language-configuration.json | 2 +- extensions/typescript-basics/language-configuration.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index cb1fb733b99..4324b1ffc2c 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -118,7 +118,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index 60814ae02f4..c54e020bf8c 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -86,7 +86,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/go/language-configuration.json b/extensions/go/language-configuration.json index 9238bf3529b..2170510547b 100644 --- a/extensions/go/language-configuration.json +++ b/extensions/go/language-configuration.json @@ -96,7 +96,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/groovy/language-configuration.json b/extensions/groovy/language-configuration.json index 39e5fd4092c..08c7a1c6395 100644 --- a/extensions/groovy/language-configuration.json +++ b/extensions/groovy/language-configuration.json @@ -74,7 +74,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/java/language-configuration.json b/extensions/java/language-configuration.json index 6ba09bbd15c..813f406fcb0 100644 --- a/extensions/java/language-configuration.json +++ b/extensions/java/language-configuration.json @@ -158,7 +158,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index 46ee043c52c..b1006f3d05f 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -231,7 +231,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index d47efe2587e..215f9d6c854 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -70,7 +70,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|[^\"]*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/less/language-configuration.json b/extensions/less/language-configuration.json index 71e155ddfcc..ff92640a6a3 100644 --- a/extensions/less/language-configuration.json +++ b/extensions/less/language-configuration.json @@ -99,7 +99,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/objective-c/language-configuration.json b/extensions/objective-c/language-configuration.json index 39e5fd4092c..08c7a1c6395 100644 --- a/extensions/objective-c/language-configuration.json +++ b/extensions/objective-c/language-configuration.json @@ -74,7 +74,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index d696ffa2950..8880f278e42 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -157,7 +157,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/rust/language-configuration.json b/extensions/rust/language-configuration.json index 490f4409c65..846c799f877 100644 --- a/extensions/rust/language-configuration.json +++ b/extensions/rust/language-configuration.json @@ -77,7 +77,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/swift/language-configuration.json b/extensions/swift/language-configuration.json index e1ceb1f6bc6..b3f8ae7de50 100644 --- a/extensions/swift/language-configuration.json +++ b/extensions/swift/language-configuration.json @@ -85,7 +85,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index 876c11e8143..b59f5ac79f0 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -249,7 +249,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "\/\/.*" + "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" }, "afterText": { "pattern": "^(?!\\s*$).+" From 373cd3886faac35154703cd301af865b2238b29a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Dec 2024 11:18:17 +0100 Subject: [PATCH 067/479] chat - allow to override status icon too (#235715) --- .../browser/parts/statusbar/statusbarPart.ts | 15 +++++++-------- .../contrib/chat/browser/chatQuotasService.ts | 6 +++++- .../services/statusbar/browser/statusbar.ts | 12 ------------ 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 7454d382d0c..efc5e92e03f 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore, disposeIfDisposable, IDisposable, MutableD import { MultiWindowParts, Part } from '../../part.js'; import { EventType as TouchEventType, Gesture, GestureEvent } from '../../../../base/browser/touch.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarStyleOverride, isStatusbarEntryLocation, IStatusbarEntryLocation, isStatusbarEntryPriority, IStatusbarEntryPriority, IStatusbarEntryOverride } from '../../../services/statusbar/browser/statusbar.js'; +import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarStyleOverride, isStatusbarEntryLocation, IStatusbarEntryLocation, isStatusbarEntryPriority, IStatusbarEntryPriority } from '../../../services/statusbar/browser/statusbar.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IAction, Separator, toAction } from '../../../../base/common/actions.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; @@ -76,10 +76,9 @@ export interface IStatusbarEntryContainer extends IDisposable { updateEntryVisibility(id: string, visible: boolean): void; /** - * Allows to override the appearance of an entry with the provided ID. Only a subset - * of properties is allowed to be overridden. + * Allows to override the appearance of an entry with the provided ID. */ - overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable; + overrideEntry(id: string, override: Partial): IDisposable; /** * Focused the status bar. If one of the status bar entries was focused, focuses it directly. @@ -141,7 +140,7 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { readonly onWillDispose = this._onWillDispose.event; private readonly onDidOverrideEntry = this._register(new Emitter()); - private readonly entryOverrides = new Map(); + private readonly entryOverrides = new Map>(); private leftItemsContainer: HTMLElement | undefined; private rightItemsContainer: HTMLElement | undefined; @@ -182,7 +181,7 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles())); } - overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable { + overrideEntry(id: string, override: Partial): IDisposable { this.entryOverrides.set(id, override); this.onDidOverrideEntry.fire(id); @@ -834,7 +833,7 @@ export class StatusbarService extends MultiWindowParts implements } } - overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable { + overrideEntry(id: string, override: Partial): IDisposable { const disposables = new DisposableStore(); for (const part of this.parts) { @@ -910,7 +909,7 @@ export class ScopedStatusbarService extends Disposable implements IStatusbarServ this.statusbarEntryContainer.updateEntryVisibility(id, visible); } - overrideEntry(id: string, override: IStatusbarEntryOverride): IDisposable { + overrideEntry(id: string, override: Partial): IDisposable { return this.statusbarEntryContainer.overrideEntry(id, override); } diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index 7b82ddbb330..87b4eb19f7f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -261,7 +261,11 @@ export class ChatQuotasStatusBarEntry extends Disposable implements IWorkbenchCo compact: isCopilotStatusVisible })); - this.entry.add(this.statusbarService.overrideEntry(ChatQuotasStatusBarEntry.COPILOT_STATUS_ID, { kind: 'prominent' })); + this.entry.add(this.statusbarService.overrideEntry(ChatQuotasStatusBarEntry.COPILOT_STATUS_ID, { + text: '$(copilot-warning)', + ariaLabel: text, + kind: 'prominent' + })); } } } diff --git a/src/vs/workbench/services/statusbar/browser/statusbar.ts b/src/vs/workbench/services/statusbar/browser/statusbar.ts index 1c589dc4f66..3b3f1f1fe17 100644 --- a/src/vs/workbench/services/statusbar/browser/statusbar.ts +++ b/src/vs/workbench/services/statusbar/browser/statusbar.ts @@ -197,15 +197,3 @@ export interface IStatusbarEntryAccessor extends IDisposable { */ update(properties: IStatusbarEntry): void; } - -/** - * A way to override a status bar entry appearance. Only a subset of - * properties are currently allowed to override. - */ -export interface IStatusbarEntryOverride { - - /** - * The kind of status bar entry. This applies different colors to the entry. - */ - readonly kind?: StatusbarEntryKind; -} From 958bcc0e643637bee7a095bfcf9a95c5f0bf83fc Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Tue, 10 Dec 2024 11:05:54 +0100 Subject: [PATCH 068/479] Remove obsolete config --- .github/commands.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .github/commands.yml diff --git a/.github/commands.yml b/.github/commands.yml deleted file mode 100644 index 5073658e526..00000000000 --- a/.github/commands.yml +++ /dev/null @@ -1,13 +0,0 @@ -{ - perform: true, - commands: - [ - { - type: "comment", - name: "findDuplicates", - allowUsers: ["cleidigh", "usernamehw", "gjsjohnmurray", "IllusionMH"], - action: "comment", - comment: "Potential duplicates:\n${potentialDuplicates}", - }, - ], -} From 037120232e42b0ac30ceab03bfceaa68b610a1d6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Dec 2024 11:48:20 +0100 Subject: [PATCH 069/479] status - tweaks to `statusBarItem.prominentHoverBackground` (#235719) --- src/vs/workbench/common/theme.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 5b0f4493d37..d864f0c9c69 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -424,12 +424,7 @@ export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem export const STATUS_BAR_PROMINENT_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.prominentHoverForeground', STATUS_BAR_ITEM_HOVER_FOREGROUND, localize('statusBarProminentItemHoverForeground', "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.prominentHoverBackground', { - dark: Color.black.transparent(0.3), - light: Color.black.transparent(0.3), - hcDark: Color.black.transparent(0.3), - hcLight: null -}, localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.prominentHoverBackground', STATUS_BAR_ITEM_HOVER_BACKGROUND, localize('statusBarProminentItemHoverBackground', "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ERROR_ITEM_BACKGROUND = registerColor('statusBarItem.errorBackground', { dark: darken(errorForeground, .4), From 461a6983d7803720547fcd04297eaa5b5f57d83f Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Tue, 10 Dec 2024 16:09:10 +0500 Subject: [PATCH 070/479] Fixes #44237: Add column number in tasks (#65264) Add column number in tasks Co-authored-by: Alex Ross --- src/vs/server/node/remoteTerminalChannel.ts | 3 +++ .../api/common/extHostVariableResolverService.ts | 9 +++++++++ .../browser/baseConfigurationResolverService.ts | 11 +++++++++++ .../common/configurationResolver.ts | 1 + .../configurationResolver/common/variableResolver.ts | 8 ++++++++ 5 files changed, 32 insertions(+) diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 399c8d8f8e6..69029595bb9 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -74,6 +74,9 @@ class CustomVariableResolver extends AbstractVariableResolverService { getLineNumber: (): string | undefined => { return resolvedVariables['lineNumber']; }, + getColumnNumber: (): string | undefined => { + return resolvedVariables['columnNumber']; + }, getExtension: async id => { const installed = await extensionService.getInstalled(); const found = installed.find(e => e.identifier.id === id); diff --git a/src/vs/workbench/api/common/extHostVariableResolverService.ts b/src/vs/workbench/api/common/extHostVariableResolverService.ts index d5c19b162fa..1acc6ab696f 100644 --- a/src/vs/workbench/api/common/extHostVariableResolverService.ts +++ b/src/vs/workbench/api/common/extHostVariableResolverService.ts @@ -117,6 +117,15 @@ class ExtHostVariableResolverService extends AbstractVariableResolverService { } return undefined; }, + getColumnNumber: (): string | undefined => { + if (editorService) { + const activeEditor = editorService.activeEditor(); + if (activeEditor) { + return String(activeEditor.selection.end.character + 1); + } + } + return undefined; + }, getExtension: (id) => { return extensionService.getExtension(id); }, diff --git a/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts index e8d38a377d1..5c06bad50b5 100644 --- a/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts @@ -121,6 +121,17 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR } return undefined; }, + getColumnNumber: (): string | undefined => { + const activeTextEditorControl = editorService.activeTextEditorControl; + if (isCodeEditor(activeTextEditorControl)) { + const selection = activeTextEditorControl.getSelection(); + if (selection) { + const columnNumber = selection.positionColumn; + return String(columnNumber); + } + } + return undefined; + }, getExtension: id => { return extensionService.getExtension(id); }, diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index fd63a52e332..e7486544888 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -95,6 +95,7 @@ export enum VariableKind { WorkspaceFolderBasename = 'workspaceFolderBasename', UserHome = 'userHome', LineNumber = 'lineNumber', + ColumnNumber = 'columnNumber', SelectedText = 'selectedText', File = 'file', FileWorkspaceFolder = 'fileWorkspaceFolder', diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index ed21fcd79b2..b67a8fe1401 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -27,6 +27,7 @@ interface IVariableResolveContext { getWorkspaceFolderPathForFile?(): string | undefined; getSelectedText(): string | undefined; getLineNumber(): string | undefined; + getColumnNumber(): string | undefined; getExtension(id: string): Promise<{ readonly extensionLocation: uri } | undefined>; } @@ -307,6 +308,13 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } throw new VariableError(VariableKind.LineNumber, localize('canNotResolveLineNumber', "Variable {0} can not be resolved. Make sure to have a line selected in the active editor.", match)); } + case 'columnNumber': { + const columnNumber = this._context.getColumnNumber(); + if (columnNumber) { + return columnNumber; + } + throw new Error(localize('canNotResolveColumnNumber', "Variable {0} can not be resolved. Make sure to have a column selected in the active editor.", match)); + } case 'selectedText': { const selectedText = this._context.getSelectedText(); if (selectedText) { From 1c91332da73c972a75c497e5d3d7bfdbf4b94684 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 10 Dec 2024 12:32:57 +0100 Subject: [PATCH 071/479] Re-adopt new YAML grammar! (#232244) * Embedded languages syntax highlighting stopped working in 1.92.0 Fixes #224978 * Get embedded grammar --- .../colorize-results/issue-1550_yaml.json | 40 +- .../colorize-results/issue-224862_yaml.json | 206 +++++- .../colorize-results/issue-4008_yaml.json | 48 +- .../colorize-results/issue-6303_yaml.json | 132 +++- .../test/colorize-results/test_yaml.json | 374 +++++++---- extensions/yaml/build/update-grammar.js | 7 +- extensions/yaml/cgmanifest.json | 31 +- extensions/yaml/package.json | 28 +- .../yaml/syntaxes/yaml-1.0.tmLanguage.json | 10 +- .../yaml/syntaxes/yaml-1.2.tmLanguage.json | 26 +- .../syntaxes/yaml-embedded.tmLanguage.json | 502 ++++++++++++++ extensions/yaml/syntaxes/yaml.tmLanguage.json | 625 ++---------------- 12 files changed, 1183 insertions(+), 846 deletions(-) create mode 100644 extensions/yaml/syntaxes/yaml-embedded.tmLanguage.json diff --git a/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json index dac84162b3c..cc1450808af 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json @@ -1,7 +1,7 @@ [ { "c": "test1", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -15,7 +15,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -29,7 +29,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": "dsd", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -71,7 +71,7 @@ }, { "c": "test2", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -85,7 +85,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "abc-def", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -141,7 +141,7 @@ }, { "c": "test-3", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -155,7 +155,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -169,7 +169,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "abcdef", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -211,7 +211,7 @@ }, { "c": "test-4", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -225,7 +225,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,7 +239,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -253,7 +253,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": "abc-def", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/issue-224862_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/issue-224862_yaml.json index 5c87b443d34..f79882e54e4 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/issue-224862_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/issue-224862_yaml.json @@ -1,7 +1,7 @@ [ { "c": "---", - "t": "source.yaml entity.other.document.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml entity.other.document.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -15,7 +15,7 @@ }, { "c": "foo", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -29,7 +29,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": "1", - "t": "source.yaml constant.numeric.integer.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml constant.numeric.integer.decimal.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -71,7 +71,7 @@ }, { "c": "---", - "t": "source.yaml entity.other.document.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml entity.other.document.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -85,7 +85,7 @@ }, { "c": "This is highlighted as a YAML string", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -99,7 +99,7 @@ }, { "c": "#", - "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -113,7 +113,7 @@ }, { "c": " This is highlighted as a YAML comment", - "t": "source.yaml comment.line.number-sign.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml comment.line.number-sign.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -126,17 +126,185 @@ } }, { - "c": "This is highlighted as a YAML error", - "t": "source.yaml string.unquoted.plain.out.yaml", + "c": "This", + "t": "source.yaml meta.stream.yaml meta.document.yaml invalid.illegal.unrecognized.yaml", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string.unquoted.plain.out.yaml: #0000FF", - "dark_vs": "string: #CE9178", - "light_vs": "string.unquoted.plain.out.yaml: #0000FF", - "hc_black": "string: #CE9178", - "dark_modern": "string: #CE9178", - "hc_light": "string.unquoted.plain.out.yaml: #0F4A85", - "light_modern": "string.unquoted.plain.out.yaml: #0000FF" + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "is", + "t": "source.yaml meta.stream.yaml meta.document.yaml invalid.illegal.unrecognized.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "highlighted", + "t": "source.yaml meta.stream.yaml meta.document.yaml invalid.illegal.unrecognized.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "as", + "t": "source.yaml meta.stream.yaml meta.document.yaml invalid.illegal.unrecognized.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "source.yaml meta.stream.yaml meta.document.yaml invalid.illegal.unrecognized.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "YAML", + "t": "source.yaml meta.stream.yaml meta.document.yaml invalid.illegal.unrecognized.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "error", + "t": "source.yaml meta.stream.yaml meta.document.yaml invalid.illegal.unrecognized.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" } } ] \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json index e1aa82e9ca3..c8c2d57d903 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json @@ -1,7 +1,7 @@ [ { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -15,7 +15,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -29,7 +29,7 @@ }, { "c": "blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -43,7 +43,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "a=\"brown,not_brown\"", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": "not_blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -127,7 +127,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -141,7 +141,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "foo", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -169,7 +169,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -211,7 +211,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -225,7 +225,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,7 +239,7 @@ }, { "c": "foo=\"}\"", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -253,7 +253,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -281,7 +281,7 @@ }, { "c": "not_blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -295,7 +295,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +309,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -323,7 +323,7 @@ }, { "c": "1", - "t": "source.yaml constant.numeric.integer.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml constant.numeric.integer.decimal.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json index 2066b677e2c..e5267d62494 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json @@ -1,7 +1,7 @@ [ { "c": "swagger", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -15,7 +15,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -29,7 +29,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -57,7 +57,7 @@ }, { "c": "2.0", - "t": "source.yaml string.quoted.single.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -71,7 +71,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "info", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -99,7 +99,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "description", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -141,7 +141,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -169,7 +169,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -183,7 +183,7 @@ }, { "c": "The API Management Service API defines an updated and refined version", - "t": "source.yaml string.quoted.single.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -196,8 +196,64 @@ } }, { - "c": " of the concepts currently known as Developer, APP, and API Product in Edge. Of", - "t": "source.yaml string.quoted.single.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string.quoted.single.yaml: #0F4A85", + "light_modern": "string.quoted.single.yaml: #0000FF" + } + }, + { + "c": "of the concepts currently known as Developer, APP, and API Product in Edge. Of", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string.quoted.single.yaml: #0F4A85", + "light_modern": "string.quoted.single.yaml: #0000FF" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -210,8 +266,8 @@ } }, { - "c": " note is the introduction of the API concept, missing previously from Edge", - "t": "source.yaml string.quoted.single.yaml", + "c": "note is the introduction of the API concept, missing previously from Edge", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -224,8 +280,22 @@ } }, { - "c": " ", - "t": "source.yaml string.quoted.single.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -239,7 +309,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -253,7 +323,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +337,7 @@ }, { "c": "title", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -281,7 +351,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -295,7 +365,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +379,7 @@ }, { "c": "API Management Service API", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -323,7 +393,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -337,7 +407,7 @@ }, { "c": "version", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -351,7 +421,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -365,7 +435,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -379,7 +449,7 @@ }, { "c": "initial", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json index 407cc7c7a1a..0908e19e3ea 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "t": "source.yaml meta.stream.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -15,7 +15,7 @@ }, { "c": " sequencer protocols for Laser eye surgery", - "t": "source.yaml comment.line.number-sign.yaml", + "t": "source.yaml meta.stream.yaml comment.line.number-sign.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -29,7 +29,7 @@ }, { "c": "---", - "t": "source.yaml entity.other.document.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml entity.other.document.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -85,7 +85,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": "&", - "t": "source.yaml meta.property.yaml keyword.control.property.anchor.yaml punctuation.definition.anchor.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.anchor.yaml punctuation.definition.anchor.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -127,21 +127,21 @@ }, { "c": "id001", - "t": "source.yaml meta.property.yaml entity.name.type.anchor.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.anchor.yaml variable.other.anchor.yaml", "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0", - "dark_modern": "entity.name.type: #4EC9B0", - "hc_light": "entity.name.type: #185E73", - "light_modern": "entity.name.type: #267F99" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" } }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "#", - "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -169,7 +169,7 @@ }, { "c": " defines anchor label &id001", - "t": "source.yaml comment.line.number-sign.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -182,8 +182,22 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +211,7 @@ }, { "c": "instrument", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -211,7 +225,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -225,7 +239,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,7 +253,7 @@ }, { "c": "Lasik 2000", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -252,8 +266,22 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +295,7 @@ }, { "c": "pulseEnergy", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -281,7 +309,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -295,7 +323,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +337,7 @@ }, { "c": "5.4", - "t": "source.yaml constant.numeric.float.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml constant.numeric.float.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -322,8 +350,22 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -337,7 +379,7 @@ }, { "c": "spotSize", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -351,7 +393,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -365,7 +407,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -379,7 +421,7 @@ }, { "c": "1mm", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -393,7 +435,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -407,7 +449,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -421,7 +463,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -435,7 +477,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -449,7 +491,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,7 +505,7 @@ }, { "c": "*", - "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -477,12 +519,12 @@ }, { "c": "id001", - "t": "source.yaml variable.other.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml variable.other.alias.yaml", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -491,7 +533,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -505,7 +547,7 @@ }, { "c": "#", - "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -519,7 +561,7 @@ }, { "c": " refers to the first step (with anchor &id001)", - "t": "source.yaml comment.line.number-sign.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -533,7 +575,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -547,7 +589,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -561,7 +603,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -575,7 +617,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -589,7 +631,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -603,7 +645,7 @@ }, { "c": "*", - "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -617,12 +659,12 @@ }, { "c": "id001", - "t": "source.yaml variable.other.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml variable.other.alias.yaml", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -630,8 +672,8 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -644,22 +686,8 @@ } }, { - "c": "spotSize", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", - "r": { - "dark_plus": "entity.name.tag: #569CD6", - "light_plus": "entity.name.tag: #800000", - "dark_vs": "entity.name.tag: #569CD6", - "light_vs": "entity.name.tag: #800000", - "hc_black": "entity.name.tag: #569CD6", - "dark_modern": "entity.name.tag: #569CD6", - "hc_light": "entity.name.tag: #0F4A85", - "light_modern": "entity.name.tag: #800000" - } - }, - { - "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -671,9 +699,23 @@ "light_modern": "default: #3B3B3B" } }, + { + "c": "spotSize:", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml invalid.illegal.unrecognized.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -687,7 +729,7 @@ }, { "c": "2mm", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -701,21 +743,21 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml punctuation.whitespace.separator.yaml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string.unquoted.plain.out.yaml: #0F4A85", + "light_modern": "string.unquoted.plain.out.yaml: #0000FF" } }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -729,7 +771,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -743,7 +785,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -757,7 +799,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -771,7 +813,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -785,7 +827,7 @@ }, { "c": "*", - "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -799,12 +841,12 @@ }, { "c": "id002", - "t": "source.yaml variable.other.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml variable.other.alias.yaml", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -813,7 +855,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -827,7 +869,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -841,7 +883,7 @@ }, { "c": "{", - "t": "source.yaml meta.flow-mapping.yaml punctuation.definition.mapping.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.definition.mapping.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -855,7 +897,7 @@ }, { "c": "name", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -869,7 +911,7 @@ }, { "c": ":", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -883,7 +925,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -897,7 +939,7 @@ }, { "c": "John Smith", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml string.unquoted.plain.in.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml string.unquoted.plain.in.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.in.yaml: #0000FF", @@ -911,7 +953,7 @@ }, { "c": ",", - "t": "source.yaml meta.flow-mapping.yaml punctuation.separator.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.separator.mapping.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +967,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -939,7 +981,7 @@ }, { "c": "age", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -953,7 +995,7 @@ }, { "c": ":", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -967,7 +1009,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -981,7 +1023,7 @@ }, { "c": "33", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml constant.numeric.integer.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml string.unquoted.plain.in.yaml constant.numeric.integer.decimal.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -995,7 +1037,7 @@ }, { "c": "}", - "t": "source.yaml meta.flow-mapping.yaml punctuation.definition.mapping.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.definition.mapping.end.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1009,7 +1051,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1023,7 +1065,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1037,7 +1079,7 @@ }, { "c": "name", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1051,7 +1093,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1065,7 +1107,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1079,7 +1121,7 @@ }, { "c": "Mary Smith", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -1093,7 +1135,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1107,7 +1149,7 @@ }, { "c": "age", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1121,7 +1163,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1135,7 +1177,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1149,7 +1191,7 @@ }, { "c": "27", - "t": "source.yaml constant.numeric.integer.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml constant.numeric.integer.decimal.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1163,7 +1205,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1176,8 +1218,22 @@ } }, { - "c": "men", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "c": "m", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml invalid.illegal.expected-indentation.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": "en", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1191,7 +1247,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1205,7 +1261,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1219,7 +1275,7 @@ }, { "c": "[", - "t": "source.yaml meta.flow-sequence.yaml punctuation.definition.sequence.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.definition.sequence.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1233,7 +1289,7 @@ }, { "c": "John Smith", - "t": "source.yaml meta.flow-sequence.yaml string.unquoted.plain.in.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml string.unquoted.plain.in.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.in.yaml: #0000FF", @@ -1247,7 +1303,7 @@ }, { "c": ",", - "t": "source.yaml meta.flow-sequence.yaml punctuation.separator.sequence.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.separator.sequence.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1261,7 +1317,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-sequence.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1275,7 +1331,7 @@ }, { "c": "Bill Jones", - "t": "source.yaml meta.flow-sequence.yaml string.unquoted.plain.in.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml string.unquoted.plain.in.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.in.yaml: #0000FF", @@ -1289,7 +1345,7 @@ }, { "c": "]", - "t": "source.yaml meta.flow-sequence.yaml punctuation.definition.sequence.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.definition.sequence.end.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1302,8 +1358,36 @@ } }, { - "c": "women", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "c": "w", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml invalid.illegal.expected-indentation.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": "o", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml invalid.illegal.expected-indentation.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": "men", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1317,7 +1401,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1331,7 +1415,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1345,7 +1429,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1359,7 +1443,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1373,7 +1457,7 @@ }, { "c": "Mary Smith", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -1387,7 +1471,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1401,7 +1485,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1415,7 +1499,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1429,7 +1513,7 @@ }, { "c": "Susan Williams", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", diff --git a/extensions/yaml/build/update-grammar.js b/extensions/yaml/build/update-grammar.js index ae390a791dd..a238c250fb3 100644 --- a/extensions/yaml/build/update-grammar.js +++ b/extensions/yaml/build/update-grammar.js @@ -7,7 +7,12 @@ var updateGrammar = require('vscode-grammar-updater'); async function updateGrammars() { - await updateGrammar.update('textmate/yaml.tmbundle', 'Syntaxes/YAML.tmLanguage', './syntaxes/yaml.tmLanguage.json', undefined); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.0.tmLanguage.json', './syntaxes/yaml-1.0.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.1.tmLanguage.json', './syntaxes/yaml-1.1.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.2.tmLanguage.json', './syntaxes/yaml-1.2.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.3.tmLanguage.json', './syntaxes/yaml-1.3.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-embedded.tmLanguage.json', './syntaxes/yaml-embedded.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml.tmLanguage.json', './syntaxes/yaml.tmLanguage.json', undefined, 'main'); } updateGrammars(); diff --git a/extensions/yaml/cgmanifest.json b/extensions/yaml/cgmanifest.json index e6c3ca158b5..be9230ef0a5 100644 --- a/extensions/yaml/cgmanifest.json +++ b/extensions/yaml/cgmanifest.json @@ -4,33 +4,24 @@ "component": { "type": "git", "git": { - "name": "textmate/yaml.tmbundle", - "repositoryUrl": "https://github.com/textmate/yaml.tmbundle", - "commitHash": "e54ceae3b719506dba7e481a77cea4a8b576ae46" + "name": "RedCMD/YAML-Syntax-Highlighter", + "repositoryUrl": "https://github.com/RedCMD/YAML-Syntax-Highlighter", + "commitHash": "53d38bbc66b704803de54ffce5b251bf97211c60" } }, "licenseDetail": [ - "Copyright (c) 2015 FichteFoll ", + "MIT License", "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", + "Copyright 2024 RedCMD", "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ], - "license": "TextMate Bundle License", - "version": "0.0.0" + "license": "MIT", + "version": "1.3.2" } ], "version": 1 diff --git a/extensions/yaml/package.json b/extensions/yaml/package.json index 6b0a787cf46..6ac315318b4 100644 --- a/extensions/yaml/package.json +++ b/extensions/yaml/package.json @@ -57,10 +57,36 @@ "scopeName": "source.yaml", "path": "./syntaxes/yaml.tmLanguage.json" }, + { + "scopeName": "source.yaml.1.3", + "path": "./syntaxes/yaml-1.3.tmLanguage.json" + }, + { + "scopeName": "source.yaml.1.2", + "path": "./syntaxes/yaml-1.2.tmLanguage.json" + }, + { + "scopeName": "source.yaml.1.1", + "path": "./syntaxes/yaml-1.1.tmLanguage.json" + }, + { + "scopeName": "source.yaml.1.0", + "path": "./syntaxes/yaml-1.0.tmLanguage.json" + }, + { + "scopeName": "source.yaml.embedded", + "path": "./syntaxes/yaml-embedded.tmLanguage.json" + }, { "language": "yaml", "scopeName": "source.yaml", - "path": "./syntaxes/yaml.tmLanguage.json" + "path": "./syntaxes/yaml.tmLanguage.json", + "unbalancedBracketScopes": [ + "invalid.illegal", + "meta.scalar.yaml", + "storage.type.tag.shorthand.yaml", + "keyword.control.flow" + ] } ], "configurationDefaults": { diff --git a/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json index 96afa88b231..9a63c206b1e 100644 --- a/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/71a88711ec184b7dde5240c8f150ad3c2dbbd5f1", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/0b50d9c47145df62c4461c4407698a24e8f1f3c2", "name": "YAML 1.0", "scopeName": "source.yaml.1.0", "comment": "https://yaml.org/spec/1.0/", @@ -520,8 +520,8 @@ "patterns": [ { "comment": "https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator", - "begin": "(?>(\\|)|(>))(?[+-])?+((0)|(1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9))(?()|([+-]))?+", - "while": "\\G(?>(?>(?!\\5)|(?!\\6) |(?!\\7) {2}|(?!\\8) {3}|(?!\\9) {4}|(?!\\10) {5}|(?!\\11) {6}|(?!\\12) {7}|(?!\\13) {8}|(?!\\14) {9})| *+($|[^#]))", + "begin": "(?>(\\|)|(>))(?[+-])?+([0-9])(?()|\\g)?+", + "while": "\\G(?> {\\4}| *+($|[^#]))", "beginCaptures": { "1": { "name": "keyword.control.flow.block-scalar.literal.yaml" @@ -534,9 +534,6 @@ }, "4": { "name": "constant.numeric.indentation-indicator.yaml" - }, - "15": { - "name": "storage.modifier.chomping-indicator.yaml" } }, "whileCaptures": { @@ -571,7 +568,6 @@ ] }, { - "comment": "https://yaml.org/spec/1.2.2/#rule-c-b-block-header", "//": "Soooooooo many edge cases", "begin": "(?>(\\|)|(>))([+-]?+)", "while": "\\G", diff --git a/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json index 5571102d9e0..d07dbe6ec5d 100644 --- a/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/71a88711ec184b7dde5240c8f150ad3c2dbbd5f1", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/53d38bbc66b704803de54ffce5b251bf97211c60", "name": "YAML 1.2", "scopeName": "source.yaml.1.2", "comment": "https://yaml.org/spec/1.2.2", @@ -38,7 +38,7 @@ }, { "comment": "For when YAML is embedded inside a Markdown code-block", - "begin": "\\G", + "begin": "\\G(?!$)", "while": "\\G", "name": "meta.stream.yaml", "patterns": [ @@ -331,10 +331,10 @@ "block-node": { "patterns": [ { - "include": "#block-sequence" + "include": "#block-mapping" }, { - "include": "#block-mapping" + "include": "#block-sequence" }, { "include": "#block-scalar" @@ -623,6 +623,10 @@ "match": "[\t ]++$", "name": "punctuation.whitespace.separator.yaml" }, + { + "match": "[^\r\n\t ](?=[\t ]*+$)", + "name": "invalid.illegal.expected-map-separator.yaml" + }, { "match": "\\x{FEFF}", "name": "invalid.illegal.bom.yaml" @@ -654,8 +658,8 @@ "patterns": [ { "comment": "https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator", - "begin": "(?>(\\|)|(>))(?[+-])?+((1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9))(?()|([+-]))?+", - "while": "\\G(?>(?>(?!\\5) |(?!\\6) {2}|(?!\\7) {3}|(?!\\8) {4}|(?!\\9) {5}|(?!\\10) {6}|(?!\\11) {7}|(?!\\12) {8}|(?!\\13) {9})| *+($|[^#]))", + "begin": "(?>(\\|)|(>))(?[+-])?+([1-9])(?()|\\g)?+", + "while": "\\G(?> {\\4}| *+($|[^#]))", "beginCaptures": { "1": { "name": "keyword.control.flow.block-scalar.literal.yaml" @@ -668,9 +672,6 @@ }, "4": { "name": "constant.numeric.indentation-indicator.yaml" - }, - "14": { - "name": "storage.modifier.chomping-indicator.yaml" } }, "whileCaptures": { @@ -705,7 +706,6 @@ ] }, { - "comment": "https://yaml.org/spec/1.2.2/#rule-c-b-block-header", "//": "Soooooooo many edge cases", "begin": "(?>(\\|)|(>))([+-]?+)", "while": "\\G", @@ -798,7 +798,7 @@ ] }, { - "comment": "Header Comment", + "comment": "https://yaml.org/spec/1.2.2/#rule-c-b-block-header", "begin": "\\G", "end": "$", "patterns": [ @@ -1279,7 +1279,7 @@ "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", "patterns": [ { - "match": "[^\t -\\x{10FFFF}]++", + "match": "[^\r\n\t -\\x{10FFFF}]++", "name": "invalid.illegal.character.yaml" }, { @@ -1614,7 +1614,7 @@ "comment": { "comment": "Comments must be separated from other tokens by white space characters. `space`, `tab`, `newline` or `carriage-return`. `#(.*)` causes performance issues", "begin": "(?<=[\\x{FEFF}\t ]|^)#", - "end": "\r|\n", + "end": "$", "captures": { "0": { "name": "punctuation.definition.comment.yaml" diff --git a/extensions/yaml/syntaxes/yaml-embedded.tmLanguage.json b/extensions/yaml/syntaxes/yaml-embedded.tmLanguage.json new file mode 100644 index 00000000000..ba7c0900784 --- /dev/null +++ b/extensions/yaml/syntaxes/yaml-embedded.tmLanguage.json @@ -0,0 +1,502 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/RedCMD/YAML-Syntax-Highlighter/blob/master/syntaxes/yaml-embedded.tmLanguage.json", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/c42cf86959ba238dc8a825bdd07bed6f5e97c978", + "name": "YAML embedded", + "scopeName": "source.yaml.embedded", + "patterns": [ + { + "include": "source.yaml.1.2#byte-order-mark" + }, + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + }, + { + "include": "#block-map-key-explicit" + }, + { + "include": "#block-map-value" + }, + { + "include": "#block-scalar" + }, + { + "include": "source.yaml.1.2#anchor-property" + }, + { + "include": "source.yaml.1.2#tag-property" + }, + { + "include": "#alias" + }, + { + "include": "source.yaml.1.2#double" + }, + { + "include": "source.yaml.1.2#single" + }, + { + "include": "source.yaml.1.2#flow-mapping" + }, + { + "include": "source.yaml.1.2#flow-sequence" + }, + { + "include": "#block-plain-out" + }, + { + "include": "#presentation-detail" + } + ], + "repository": { + "directives": { + "comment": "https://yaml.org/spec/1.2.2/#68-directives", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "(?>^|\\G)(%)(YAML)([\t ]+)([0-9]+\\.[0-9]*)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.yaml.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "constant.numeric.yaml-version.yaml" + } + }, + "name": "meta.directives.yaml", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#682-tag-directives", + "begin": "(?>^|\\G)(%)(TAG)(?>([\t ]++)((!)(?>[0-9A-Za-z-]*+(!))?+))?+", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.tag.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "storage.type.tag-handle.yaml" + }, + "5": { + "name": "punctuation.definition.tag.begin.yaml" + }, + "6": { + "name": "punctuation.definition.tag.end.yaml" + }, + "comment": "https://yaml.org/spec/1.2.2/#rule-c-tag-handle" + }, + "patterns": [ + { + "comment": "technically the beginning should only validate against a valid uri scheme [A-Za-z][A-Za-z0-9.+-]*", + "begin": "\\G[\t ]++(?!#)", + "end": "(?=[\r\n\t ])", + "beginCaptures": { + "0": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "contentName": "support.type.tag-prefix.yaml", + "patterns": [ + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "\\G[,\\[\\]{}]", + "name": "invalid.illegal.character.uri.yaml" + }, + { + "include": "source.yaml#non-printable" + }, + { + "match": "[^\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.!~*'()\\[\\]]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-reserved-directive", + "begin": "(?>^|\\G)(%)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.other.yaml" + } + }, + "patterns": [ + { + "match": "\\G([\t ]++)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-name.yaml" + } + } + }, + { + "match": "([\t ]++)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-parameter.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "document": { + "comment": "https://yaml.org/spec/1.2.2/#91-documents", + "patterns": [ + { + "match": "(?>^|\\G)---(?=[\r\n\t ])", + "name": "entity.other.document.begin.yaml" + }, + { + "begin": "(?>^|\\G)\\.{3}(?=[\r\n\t ])", + "end": "$", + "name": "entity.other.document.end.yaml", + "patterns": [ + { + "include": "#presentation-detail" + }, + { + "include": "source.yaml.1.2#unknown" + } + ] + } + ] + }, + "block-mapping": { + "//": "The check for plain keys is expensive", + "begin": "(?<=^|\\G|\t| )(?(?#Double Quote)\"(?>[^\\\\\"]++|\\\\.)*+\"|(?#Single Quote)'(?>[^']++|'')*+'|(?#Flow-Map){(?>[^}]++|}[ \t]*+(?!:[\r\n\t ]))++}|(?#Flow-Seq)\\[(?>[^]]++|][ \t]*+(?!:[\r\n\t ]))++]|(?#Plain)(?>[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))(?>[^:#]++|:(?![\r\n\t ])|(?:|(?>^|\\G)(?>\\.{3}|---))[\r\n\t ])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.map.explicit.yaml", + "patterns": [ + { + "include": "source.yaml.1.2#key-double" + }, + { + "include": "source.yaml#key-single" + }, + { + "include": "#flow-key-plain-out" + }, + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + }, + { + "include": "#block-scalar" + }, + { + "include": "source.yaml.1.2#anchor-property" + }, + { + "include": "source.yaml.1.2#tag-property" + }, + { + "include": "#alias" + }, + { + "include": "source.yaml.1.2#flow-mapping" + }, + { + "include": "source.yaml.1.2#flow-sequence" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-map-value": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", + "match": ":(?=[\r\n\t ])", + "name": "punctuation.separator.map.value.yaml" + }, + "block-scalar": { + "patterns": [ + { + "comment": "This doesn't work correctly when indented. Might have to dump it", + "begin": "(?>(\\|)|(>))(?[+-])?+([1-9])(?()|\\g)?+", + "while": "(?>^|\\G)(?> {\\4}| *+$)", + "beginCaptures": { + "1": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "3": { + "name": "storage.modifier.chomping-indicator.yaml" + }, + "4": { + "name": "constant.numeric.indentation-indicator.yaml" + } + }, + "whileCaptures": { + "0": { + "name": "punctuation.whitespace.indentation.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "begin": "$", + "while": "\\G", + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "source.yaml#non-printable" + } + ] + }, + { + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + }, + { + "include": "source.yaml.1.2#unknown" + } + ] + } + ] + }, + { + "comment": "I'm not sure how I feel about this", + "begin": "(?>(\\|)|(>))([+-]?+)(.*+)", + "end": "(?! |$)", + "beginCaptures": { + "1": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "3": { + "name": "storage.modifier.chomping-indicator.yaml" + }, + "4": { + "patterns": [ + { + "include": "#presentation-detail" + }, + { + "include": "source.yaml.1.2#unknown" + } + ] + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "//": "Find the highest indented line", + "begin": "(?>^|\\G)(?=( ++)$)", + "end": "(?>^|\\G)(?>(?=\\1(?!$))|(?!\\1| *+$) *+)", + "endCaptures": { + "0": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "begin": "(?>^|\\G)(?=( ++))", + "end": "(?>^|\\G)(?!\\1| *+$) *+", + "endCaptures": { + "0": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "comment": "This is not 100% correct", + "match": "(?>^|\\G) ++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "source.yaml#non-printable" + } + ] + } + ] + } + ] + }, + "block-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-OUT)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "end": "(?=[\t ]++#|[\t ]*+$)", + "name": "string.unquoted.plain.out.yaml", + "patterns": [ + { + "include": "source.yaml.1.2#tag-implicit-plain-out" + }, + { + "match": ":(?=[\r\n\t ])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "source.yaml#non-printable" + } + ] + }, + "flow-key-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (FLOW-OUT)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "end": "(?=[\t ]*+(?>$|:[\r\n\t ])|[\t ]++#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "source.yaml.1.2#tag-implicit-plain-out" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "source.yaml#non-printable" + } + ] + }, + "alias": { + "match": "(\\*)([\\x{85}[^ ,\\[\\]{}\\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)|(\\*)", + "captures": { + "0": { + "name": "keyword.control.flow.alias.yaml" + }, + "1": { + "name": "punctuation.definition.alias.yaml" + }, + "2": { + "name": "variable.other.alias.yaml" + }, + "3": { + "name": "invalid.illegal.flow.alias.yaml" + } + } + }, + "presentation-detail": { + "patterns": [ + { + "match": "[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "source.yaml#non-printable" + }, + { + "include": "source.yaml.1.2#comment" + } + ] + } + } +} \ No newline at end of file diff --git a/extensions/yaml/syntaxes/yaml.tmLanguage.json b/extensions/yaml/syntaxes/yaml.tmLanguage.json index 447df713901..168cd4f7649 100644 --- a/extensions/yaml/syntaxes/yaml.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml.tmLanguage.json @@ -1,621 +1,116 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/textmate/yaml.tmbundle/blob/master/Syntaxes/YAML.tmLanguage", + "This file has been converted from https://github.com/RedCMD/YAML-Syntax-Highlighter/blob/master/syntaxes/yaml.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/yaml.tmbundle/commit/e54ceae3b719506dba7e481a77cea4a8b576ae46", - "name": "YAML", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/c42cf86959ba238dc8a825bdd07bed6f5e97c978", + "name": "YAML Ain't Markup Language", "scopeName": "source.yaml", "patterns": [ { - "include": "#comment" - }, - { - "include": "#property" - }, - { - "include": "#directive" - }, - { - "match": "^---", - "name": "entity.other.document.begin.yaml" - }, - { - "match": "^\\.{3}", - "name": "entity.other.document.end.yaml" - }, - { - "include": "#node" - } - ], - "repository": { - "block-collection": { - "patterns": [ - { - "include": "#block-sequence" - }, - { - "include": "#block-mapping" - } - ] - }, - "block-mapping": { - "patterns": [ - { - "include": "#block-pair" - } - ] - }, - "block-node": { - "patterns": [ - { - "include": "#prototype" - }, - { - "include": "#block-scalar" - }, - { - "include": "#block-collection" - }, - { - "include": "#flow-scalar-plain-out" - }, - { - "include": "#flow-node" - } - ] - }, - "block-pair": { - "patterns": [ - { - "begin": "\\?", - "beginCaptures": { - "1": { - "name": "punctuation.definition.key-value.begin.yaml" - } - }, - "end": "(?=\\?)|^ *(:)|(:)", - "endCaptures": { - "1": { - "name": "punctuation.separator.key-value.mapping.yaml" - }, - "2": { - "name": "invalid.illegal.expected-newline.yaml" - } - }, - "name": "meta.block-mapping.yaml", - "patterns": [ - { - "include": "#block-node" - } - ] - }, - { - "begin": "(?x)\n (?=\n (?x:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n )\n (\n [^\\s:]\n | : \\S\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", - "patterns": [ - { - "include": "#flow-scalar-plain-out-implicit-type" - }, - { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", - "beginCaptures": { - "0": { - "name": "entity.name.tag.yaml" - } - }, - "contentName": "entity.name.tag.yaml", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", - "name": "string.unquoted.plain.out.yaml" - } - ] - }, - { - "match": ":(?=\\s|$)", - "name": "punctuation.separator.key-value.mapping.yaml" - } - ] - }, - "block-scalar": { - "begin": "(?:(\\|)|(>))([1-9])?([-+])?(.*\\n?)", - "beginCaptures": { - "1": { - "name": "keyword.control.flow.block-scalar.literal.yaml" - }, - "2": { - "name": "keyword.control.flow.block-scalar.folded.yaml" - }, - "3": { - "name": "constant.numeric.indentation-indicator.yaml" - }, - "4": { - "name": "storage.modifier.chomping-indicator.yaml" - }, - "5": { - "patterns": [ - { - "include": "#comment" - }, - { - "match": ".+", - "name": "invalid.illegal.expected-comment-or-newline.yaml" - } - ] - } - }, - "end": "^(?=\\S)|(?!\\G)", - "patterns": [ - { - "begin": "^([ ]+)(?! )", - "end": "^(?!\\1|\\s*$)", - "name": "string.unquoted.block.yaml" - } - ] - }, - "block-sequence": { - "match": "(-)(?!\\S)", - "name": "punctuation.definition.block.sequence.item.yaml" - }, - "comment": { - "begin": "(?:(^[ \\t]*)|[ \\t]+)(?=#\\p{Print}*$)", - "beginCaptures": { - "1": { - "name": "punctuation.whitespace.comment.leading.yaml" - } - }, - "end": "(?!\\G)", - "patterns": [ - { - "begin": "#", - "beginCaptures": { - "0": { - "name": "punctuation.definition.comment.yaml" - } - }, - "end": "\\n", - "name": "comment.line.number-sign.yaml" - } - ] - }, - "directive": { - "begin": "^%", - "beginCaptures": { - "0": { - "name": "punctuation.definition.directive.begin.yaml" - } - }, - "end": "(?=$|[ \\t]+($|#))", - "name": "meta.directive.yaml", - "patterns": [ - { - "captures": { - "1": { - "name": "keyword.other.directive.yaml.yaml" - }, - "2": { - "name": "constant.numeric.yaml-version.yaml" - } - }, - "match": "\\G(YAML)[ \\t]+(\\d+\\.\\d+)" - }, - { - "captures": { - "1": { - "name": "keyword.other.directive.tag.yaml" - }, - "2": { - "name": "storage.type.tag-handle.yaml" - }, - "3": { - "name": "support.type.tag-prefix.yaml" - } - }, - "match": "(?x)\n \\G\n (TAG)\n (?:[ \\t]+\n ((?:!(?:[0-9A-Za-z\\-]*!)?))\n (?:[ \\t]+ (\n ! (?x: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )*\n | (?![,!\\[\\]{}]) (?x: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )+\n )\n )?\n )?\n " - }, - { - "captures": { - "1": { - "name": "support.other.directive.reserved.yaml" - }, - "2": { - "name": "string.unquoted.directive-name.yaml" - }, - "3": { - "name": "string.unquoted.directive-parameter.yaml" - } - }, - "match": "(?x) \\G (\\w+) (?:[ \\t]+ (\\w+) (?:[ \\t]+ (\\w+))? )?" - }, - { - "match": "\\S+", - "name": "invalid.illegal.unrecognized.yaml" - } - ] - }, - "flow-alias": { - "captures": { - "1": { - "name": "keyword.control.flow.alias.yaml" - }, - "2": { - "name": "punctuation.definition.alias.yaml" - }, - "3": { - "name": "variable.other.alias.yaml" - }, - "4": { - "name": "invalid.illegal.character.anchor.yaml" - } - }, - "match": "((\\*))([^\\s\\[\\]/{/},]+)([^\\s\\]},]\\S*)?" - }, - "flow-collection": { - "patterns": [ - { - "include": "#flow-sequence" - }, - { - "include": "#flow-mapping" - } - ] - }, - "flow-mapping": { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.definition.mapping.begin.yaml" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.definition.mapping.end.yaml" - } - }, - "name": "meta.flow-mapping.yaml", - "patterns": [ - { - "include": "#prototype" - }, - { - "match": ",", - "name": "punctuation.separator.mapping.yaml" - }, - { - "include": "#flow-pair" - } - ] - }, - "flow-node": { + "comment": "Default to YAML version 1.2", + "begin": "\\A", + "while": "^", "patterns": [ { - "include": "#prototype" - }, - { - "include": "#flow-alias" - }, - { - "include": "#flow-collection" - }, - { - "include": "#flow-scalar" + "include": "source.yaml.1.2" } ] }, - "flow-pair": { + { + "comment": "Support legacy FrontMatter integration", + "//": "https://github.com/microsoft/vscode-markdown-tm-grammar/pull/162", + "begin": "(?<=^-{3,}\\s*+)\\G$", + "while": "^(?! {3,0}-{3,}[ \t]*+$|[ \t]*+\\.{3}$)", "patterns": [ { - "begin": "\\?", - "beginCaptures": { - "0": { - "name": "punctuation.definition.key-value.begin.yaml" - } - }, - "end": "(?=[},\\]])", - "name": "meta.flow-pair.explicit.yaml", - "patterns": [ - { - "include": "#prototype" - }, - { - "include": "#flow-pair" - }, - { - "include": "#flow-node" - }, - { - "begin": ":(?=\\s|$|[\\[\\]{},])", - "beginCaptures": { - "0": { - "name": "punctuation.separator.key-value.mapping.yaml" - } - }, - "end": "(?=[},\\]])", - "patterns": [ - { - "include": "#flow-value" - } - ] - } - ] - }, - { - "begin": "(?x)\n (?=\n (?:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n )\n (\n [^\\s:[\\[\\]{},]]\n | : [^\\s[\\[\\]{},]]\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", - "name": "meta.flow-pair.key.yaml", - "patterns": [ - { - "include": "#flow-scalar-plain-in-implicit-type" - }, - { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", - "beginCaptures": { - "0": { - "name": "entity.name.tag.yaml" - } - }, - "contentName": "entity.name.tag.yaml", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", - "name": "string.unquoted.plain.in.yaml" - } - ] - }, - { - "include": "#flow-node" - }, - { - "begin": ":(?=\\s|$|[\\[\\]{},])", - "captures": { - "0": { - "name": "punctuation.separator.key-value.mapping.yaml" - } - }, - "end": "(?=[},\\]])", - "name": "meta.flow-pair.yaml", - "patterns": [ - { - "include": "#flow-value" - } - ] + "include": "source.yaml.1.2" } ] }, - "flow-scalar": { - "patterns": [ - { - "include": "#flow-scalar-double-quoted" - }, - { - "include": "#flow-scalar-single-quoted" - }, - { - "include": "#flow-scalar-plain-in" - } - ] + { + "comment": "Basic version for embedding", + "include": "source.yaml.embedded" + } + ], + "repository": { + "parity": { + "comment": "Yes... That is right. Due to the changes with \\x2028, \\x2029, \\x85 and 'tags'. This is all the code I was able to reuse between all YAML versions 1.3, 1.2, 1.1 and 1.0" }, - "flow-scalar-double-quoted": { - "begin": "\"", + "block-map-key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.yaml" } }, - "end": "\"", "endCaptures": { "0": { "name": "punctuation.definition.string.end.yaml" } }, - "name": "string.quoted.double.yaml", - "patterns": [ - { - "match": "\\\\([0abtnvfre \"/\\\\N_Lp]|x\\d\\d|u\\d{4}|U\\d{8})", - "name": "constant.character.escape.yaml" - }, - { - "match": "\\\\\\n", - "name": "constant.character.escape.double-quoted.newline.yaml" - } - ] - }, - "flow-scalar-plain-in": { + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", "patterns": [ { - "include": "#flow-scalar-plain-in-implicit-type" + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" }, { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", - "name": "string.unquoted.plain.in.yaml" - } - ] - }, - "flow-scalar-plain-in-implicit-type": { - "patterns": [ - { - "captures": { - "1": { - "name": "constant.language.null.yaml" - }, - "2": { - "name": "constant.language.boolean.yaml" - }, - "3": { - "name": "constant.numeric.integer.yaml" - }, - "4": { - "name": "constant.numeric.float.yaml" - }, - "5": { - "name": "constant.other.timestamp.yaml" - }, - "6": { - "name": "constant.language.value.yaml" - }, - "7": { - "name": "constant.language.merge.yaml" - } - }, - "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n )\n " - } - ] - }, - "flow-scalar-plain-out": { - "patterns": [ - { - "include": "#flow-scalar-plain-out-implicit-type" + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" }, { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", - "name": "string.unquoted.plain.out.yaml" - } - ] - }, - "flow-scalar-plain-out-implicit-type": { - "patterns": [ - { - "captures": { - "1": { - "name": "constant.language.null.yaml" - }, - "2": { - "name": "constant.language.boolean.yaml" - }, - "3": { - "name": "constant.numeric.integer.yaml" - }, - "4": { - "name": "constant.numeric.float.yaml" - }, - "5": { - "name": "constant.other.timestamp.yaml" - }, - "6": { - "name": "constant.language.value.yaml" - }, - "7": { - "name": "constant.language.merge.yaml" - } - }, - "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?x:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n )\n " + "match": "''", + "name": "constant.character.escape.single-quote.yaml" } ] }, - "flow-scalar-single-quoted": { + "key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (FLOW-OUT)", "begin": "'", + "end": "'(?!')", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.yaml" } }, - "end": "'(?!')", "endCaptures": { "0": { "name": "punctuation.definition.string.end.yaml" } }, - "name": "string.quoted.single.yaml", + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", "patterns": [ { - "match": "''", - "name": "constant.character.escape.single-quoted.yaml" - } - ] - }, - "flow-sequence": { - "begin": "\\[", - "beginCaptures": { - "0": { - "name": "punctuation.definition.sequence.begin.yaml" - } - }, - "end": "\\]", - "endCaptures": { - "0": { - "name": "punctuation.definition.sequence.end.yaml" - } - }, - "name": "meta.flow-sequence.yaml", - "patterns": [ - { - "include": "#prototype" - }, - { - "match": ",", - "name": "punctuation.separator.sequence.yaml" + "match": "[^\r\n\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" }, { - "include": "#flow-pair" - }, - { - "include": "#flow-node" - } - ] - }, - "flow-value": { - "patterns": [ - { - "begin": "\\G(?![},\\]])", - "end": "(?=[},\\]])", - "name": "meta.flow-pair.value.yaml", - "patterns": [ - { - "include": "#flow-node" - } - ] - } - ] - }, - "node": { - "patterns": [ - { - "include": "#block-node" - } - ] - }, - "property": { - "begin": "(?=!|&)", - "end": "(?!\\G)", - "name": "meta.property.yaml", - "patterns": [ - { - "captures": { - "1": { - "name": "keyword.control.property.anchor.yaml" - }, - "2": { - "name": "punctuation.definition.anchor.yaml" - }, - "3": { - "name": "entity.name.type.anchor.yaml" - }, - "4": { - "name": "invalid.illegal.character.anchor.yaml" - } - }, - "match": "\\G((&))([^\\s\\[\\]/{/},]+)(\\S+)?" - }, - { - "match": "(?x)\n \\G\n (?:\n ! < (?: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )+ >\n | (?:!(?:[0-9A-Za-z\\-]*!)?) (?: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$_.~*'()] )+\n | !\n )\n (?=\\ |\\t|$)\n ", - "name": "storage.type.tag-handle.yaml" - }, - { - "match": "\\S+", - "name": "invalid.illegal.tag-handle.yaml" - } - ] - }, - "prototype": { - "patterns": [ - { - "include": "#comment" - }, - { - "include": "#property" - } - ] + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + "non-printable": { + "//": { + "85": "…", + "2028": "", + "2029": "", + "10000": "𐀀", + "A0": " ", + "D7FF": "퟿", + "E000": "", + "FFFD": "�", + "FEFF": "", + "FFFF": "￿", + "10FFFF": "􏿿" + }, + "//match": "[\\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}&&[^\t\n\r\\x{85}]]++", + "match": "[^\t\n\r -~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]++", + "name": "invalid.illegal.non-printable.yaml" } } } \ No newline at end of file From 8a2a20c476e27f6f931cc322d4d6a98ebba80581 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Dec 2024 12:44:49 +0100 Subject: [PATCH 072/479] Hovering on Copilot link is now showing the link href (fix microsoft/vscode-copilot#11189) (#235724) --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 3e43f4428ec..9713d25a97f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -840,7 +840,7 @@ class ChatSetupWelcomeContent extends Disposable { // Header { - const header = localize({ key: 'setupHeader', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0} 'Copilot') is your AI pair programmer.", this.context.state.installed ? 'command:github.copilot.open.walkthrough' : defaultChat.documentationUrl); + const header = localize({ key: 'setupHeader', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer.", this.context.state.installed ? 'command:github.copilot.open.walkthrough' : defaultChat.documentationUrl); this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(header, { isTrusted: true }))).element); const features = this.element.appendChild($('div.chat-features-container')); From 4d5225f1fca6a194b48473df0d62e12cef624758 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 10 Dec 2024 07:08:33 -0800 Subject: [PATCH 073/479] Mention shell integration in terminal sticky scroll setting Fixes #200509 --- .../stickyScroll/common/terminalStickyScrollConfiguration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration.ts index 9a12add2442..47f043767c8 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration.ts @@ -7,6 +7,7 @@ import type { IStringDictionary } from '../../../../../base/common/collections.j import { localize } from '../../../../../nls.js'; import type { IConfigurationPropertySchema } from '../../../../../platform/configuration/common/configurationRegistry.js'; import product from '../../../../../platform/product/common/product.js'; +import { TerminalSettingId } from '../../../../../platform/terminal/common/terminal.js'; export const enum TerminalStickyScrollSettingId { Enabled = 'terminal.integrated.stickyScroll.enabled', @@ -20,7 +21,7 @@ export interface ITerminalStickyScrollConfiguration { export const terminalStickyScrollConfiguration: IStringDictionary = { [TerminalStickyScrollSettingId.Enabled]: { - markdownDescription: localize('stickyScroll.enabled', "Shows the current command at the top of the terminal."), + markdownDescription: localize('stickyScroll.enabled', "Shows the current command at the top of the terminal. This feature requires [shell integration]({0}) to be activated. See {1}.", 'https://code.visualstudio.com/docs/terminal/shell-integration', `\`#${TerminalSettingId.ShellIntegrationEnabled}#\``), type: 'boolean', default: product.quality !== 'stable' }, From 97d42fd4b813382c253c1b362a81b753b84e986d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 Dec 2024 16:22:56 +0100 Subject: [PATCH 074/479] fix #197849 - improve doc (#235736) --- src/vscode-dts/vscode.d.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index d49cd276997..4fd34315755 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -13986,10 +13986,13 @@ declare module 'vscode' { } /** - * The configuration scope which can be a - * a 'resource' or a languageId or both or - * a '{@link TextDocument}' or - * a '{@link WorkspaceFolder}' + * The configuration scope which can be: + * - a {@link Uri} representing a resource + * - a {@link TextDocument} representing an open text document + * - a {@link WorkspaceFolder} representing a workspace folder + * - an object containing: + * - `uri`: an optional {@link Uri} of a text document + * - `languageId`: the language identifier of a text document */ export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { /** From 7d69a1c962175789eb2e58811b0551108ff6db24 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 10 Dec 2024 16:38:03 +0100 Subject: [PATCH 075/479] Allow accepting a path in the simple file picker when items haven't been listed (#235740) Fixes #232950 --- src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 843d9ed0a0c..c4faf15f40b 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -497,7 +497,7 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { private async onDidAccept(): Promise { this.busy = true; - if (this.filePickBox.activeItems.length === 1) { + if (!this.updatingPromise && this.filePickBox.activeItems.length === 1) { const item = this.filePickBox.selectedItems[0]; if (item.isFolder) { if (this.trailing) { @@ -519,7 +519,7 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { this.filePickBox.busy = false; return; } - } else { + } else if (!this.updatingPromise) { // If the items have updated, don't try to resolve if ((await this.tryUpdateItems(this.filePickBox.value, this.filePickBoxValue())) !== UpdateResult.NotUpdated) { this.filePickBox.busy = false; From c0d25ffb56b4b1fe4a4f33ab764f0fff1970d66d Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 10 Dec 2024 16:49:07 +0100 Subject: [PATCH 076/479] Performance regression: revert "Ignoring double slashes inside of strings" (#235742) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "onEnter Indentation Rules: Ignoring double slashes inside of strings …" This reverts commit 21c52a4a3ae29fac345c863b39c5fd44f4005862. --- extensions/cpp/language-configuration.json | 2 +- extensions/csharp/language-configuration.json | 2 +- extensions/go/language-configuration.json | 2 +- extensions/groovy/language-configuration.json | 2 +- extensions/java/language-configuration.json | 2 +- extensions/javascript/javascript-language-configuration.json | 2 +- extensions/json/language-configuration.json | 2 +- extensions/less/language-configuration.json | 2 +- extensions/objective-c/language-configuration.json | 2 +- extensions/php/language-configuration.json | 2 +- extensions/rust/language-configuration.json | 2 +- extensions/swift/language-configuration.json | 2 +- extensions/typescript-basics/language-configuration.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index 4324b1ffc2c..cb1fb733b99 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -118,7 +118,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index c54e020bf8c..60814ae02f4 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -86,7 +86,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/go/language-configuration.json b/extensions/go/language-configuration.json index 2170510547b..9238bf3529b 100644 --- a/extensions/go/language-configuration.json +++ b/extensions/go/language-configuration.json @@ -96,7 +96,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/groovy/language-configuration.json b/extensions/groovy/language-configuration.json index 08c7a1c6395..39e5fd4092c 100644 --- a/extensions/groovy/language-configuration.json +++ b/extensions/groovy/language-configuration.json @@ -74,7 +74,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/java/language-configuration.json b/extensions/java/language-configuration.json index 813f406fcb0..6ba09bbd15c 100644 --- a/extensions/java/language-configuration.json +++ b/extensions/java/language-configuration.json @@ -158,7 +158,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index b1006f3d05f..46ee043c52c 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -231,7 +231,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index 215f9d6c854..d47efe2587e 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -70,7 +70,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|[^\"]*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/less/language-configuration.json b/extensions/less/language-configuration.json index ff92640a6a3..71e155ddfcc 100644 --- a/extensions/less/language-configuration.json +++ b/extensions/less/language-configuration.json @@ -99,7 +99,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/objective-c/language-configuration.json b/extensions/objective-c/language-configuration.json index 08c7a1c6395..39e5fd4092c 100644 --- a/extensions/objective-c/language-configuration.json +++ b/extensions/objective-c/language-configuration.json @@ -74,7 +74,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index 8880f278e42..d696ffa2950 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -157,7 +157,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/rust/language-configuration.json b/extensions/rust/language-configuration.json index 846c799f877..490f4409c65 100644 --- a/extensions/rust/language-configuration.json +++ b/extensions/rust/language-configuration.json @@ -77,7 +77,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/swift/language-configuration.json b/extensions/swift/language-configuration.json index b3f8ae7de50..e1ceb1f6bc6 100644 --- a/extensions/swift/language-configuration.json +++ b/extensions/swift/language-configuration.json @@ -85,7 +85,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|[^\"']*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index b59f5ac79f0..876c11e8143 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -249,7 +249,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "^(?:\"[^\"]*\"|'[^']*'|`[^`]*`|[^\"'`]*)*\/\/" + "pattern": "\/\/.*" }, "afterText": { "pattern": "^(?!\\s*$).+" From d239906998fca67302d6c325b709050f4ce767dd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 Dec 2024 16:49:51 +0100 Subject: [PATCH 077/479] fix #227358 (#235743) --- .../userDataProfile/common/remoteUserDataProfiles.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/userDataProfile/common/remoteUserDataProfiles.ts b/src/vs/workbench/services/userDataProfile/common/remoteUserDataProfiles.ts index 1281d1f717b..5c9ca3c72cc 100644 --- a/src/vs/workbench/services/userDataProfile/common/remoteUserDataProfiles.ts +++ b/src/vs/workbench/services/userDataProfile/common/remoteUserDataProfiles.ts @@ -15,6 +15,7 @@ import { IUserDataProfileService } from './userDataProfile.js'; import { distinct } from '../../../../base/common/arrays.js'; import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js'; import { UserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfileIpc.js'; +import { ErrorNoTelemetry } from '../../../../base/common/errors.js'; const associatedRemoteProfilesKey = 'associatedRemoteProfiles'; @@ -81,7 +82,7 @@ class RemoteUserDataProfilesService extends Disposable implements IRemoteUserDat await this.initPromise; if (!this.remoteUserDataProfilesService) { - throw new Error('Remote profiles service not available in the current window'); + throw new ErrorNoTelemetry('Remote profiles service not available in the current window'); } return this.remoteUserDataProfilesService.profiles; @@ -91,7 +92,7 @@ class RemoteUserDataProfilesService extends Disposable implements IRemoteUserDat await this.initPromise; if (!this.remoteUserDataProfilesService) { - throw new Error('Remote profiles service not available in the current window'); + throw new ErrorNoTelemetry('Remote profiles service not available in the current window'); } return this.getAssociatedRemoteProfile(localProfile, this.remoteUserDataProfilesService); From f43f3cc8f962d7fc4e8fa6ce5878882935cbaf1c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 10 Dec 2024 17:26:34 +0100 Subject: [PATCH 078/479] server.cli.ts does not await Promises (#235738) --- resources/server/bin-dev/remote-cli/code.cmd | 1 + resources/server/bin-dev/remote-cli/code.sh | 2 ++ src/server-cli.ts | 2 +- src/vs/server/node/server.cli.ts | 14 +++++++------- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/resources/server/bin-dev/remote-cli/code.cmd b/resources/server/bin-dev/remote-cli/code.cmd index fd1d2c5ca49..4d8214ead19 100644 --- a/resources/server/bin-dev/remote-cli/code.cmd +++ b/resources/server/bin-dev/remote-cli/code.cmd @@ -1,6 +1,7 @@ @echo off setlocal SET VSCODE_PATH=%~dp0..\..\..\.. +SET VSCODE_DEV=1 FOR /F "tokens=* USEBACKQ" %%g IN (`where /r "%VSCODE_PATH%\.build\node" node.exe`) do (SET "NODE=%%g") call "%NODE%" "%VSCODE_PATH%\out\server-cli.js" "Code Server - Dev" "" "" "code.cmd" %* endlocal diff --git a/resources/server/bin-dev/remote-cli/code.sh b/resources/server/bin-dev/remote-cli/code.sh index 1d0bc9b5c85..3deeb272c7c 100755 --- a/resources/server/bin-dev/remote-cli/code.sh +++ b/resources/server/bin-dev/remote-cli/code.sh @@ -10,6 +10,8 @@ else VSCODE_PATH=$(dirname $(dirname $(dirname $(dirname $(dirname $(readlink -f $0)))))) fi +export VSCODE_DEV=1 + PROD_NAME="Code Server - Dev" VERSION="" COMMIT="" diff --git a/src/server-cli.ts b/src/server-cli.ts index 3ed0c87a466..4d1afa4018a 100644 --- a/src/server-cli.ts +++ b/src/server-cli.ts @@ -30,4 +30,4 @@ if (process.env['VSCODE_DEV']) { await bootstrapESM(); // Load Server -await import('./vs/server/node/server.cli'); +await import('./vs/server/node/server.cli.js'); diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index eb68e8aa8b5..654a769cd27 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -154,7 +154,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise { console.log(res); @@ -305,7 +305,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise { From f20fe4d31a589ee118632b9663e4b4cb4dc01587 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 10 Dec 2024 17:38:39 +0100 Subject: [PATCH 079/479] Typing ".." in the (simple) Open File dialog has strange behaviour (#235746) Fixes #154642 --- src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index c4faf15f40b..0fc6fad2c5a 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -862,6 +862,8 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { private async updateItems(newFolder: URI, force: boolean = false, trailing?: string): Promise { this.busy = true; this.autoCompletePathSegment = ''; + const wasDotDot = trailing === '..'; + trailing = wasDotDot ? undefined : trailing; const isSave = !!trailing; let result = false; @@ -892,7 +894,7 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { this.filePickBox.items = items; // the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory. - if (!equalsIgnoreCase(this.filePickBox.value, newValue) && force) { + if (!equalsIgnoreCase(this.filePickBox.value, newValue) && (force || wasDotDot)) { this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; this.insertText(newValue, newValue); } From a5539aee23533a8083819cb0b4277b05e1d6525a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:41:48 -0800 Subject: [PATCH 080/479] Clarify sixel support on Windows, mention conpty version Fixes #198622 --- .../contrib/terminal/common/terminalConfiguration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 752b9e8d1bd..54c79186f02 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -460,7 +460,7 @@ const terminalConfiguration: IConfigurationNode = { default: true }, [TerminalSettingId.ExperimentalWindowsUseConptyDll]: { - markdownDescription: localize('terminal.integrated.experimentalWindowsUseConptyDll', "Whether to use the experimental conpty.dll shipped with VS Code, instead of the one bundled with Windows."), + markdownDescription: localize('terminal.integrated.experimentalWindowsUseConptyDll', "Whether to use the experimental conpty.dll (v1.20.240626001) shipped with VS Code, instead of the one bundled with Windows."), type: 'boolean', default: false }, @@ -590,7 +590,7 @@ const terminalConfiguration: IConfigurationNode = { }, [TerminalSettingId.EnableImages]: { restricted: true, - markdownDescription: localize('terminal.integrated.enableImages', "Enables image support in the terminal, this will only work when {0} is enabled. Both sixel and iTerm's inline image protocol are supported on Linux and macOS, Windows support will light up automatically when ConPTY passes through the sequences. Images will currently not be restored between window reloads/reconnects.", `\`#${TerminalSettingId.GpuAcceleration}#\``), + markdownDescription: localize('terminal.integrated.enableImages', "Enables image support in the terminal, this will only work when {0} is enabled. Both sixel and iTerm's inline image protocol are supported on Linux and macOS. This will only work on Windows for versions of ConPTY >= v2 which is shipped with Windows itself, see also {1}. Images will currently not be restored between window reloads/reconnects.", `\`#${TerminalSettingId.GpuAcceleration}#\``, `\`#${TerminalSettingId.ExperimentalWindowsUseConptyDll}#\``), type: 'boolean', default: false }, From 3e59d46a8708ed5baf367416778de566f7ab39d1 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 10 Dec 2024 13:55:59 -0600 Subject: [PATCH 081/479] testing: sort completed tests by start sequence, not completion time (#235758) * testing: sort completed tests by start sequence, not completion time Fixes #235667 The root cause of the issue is the separate test run that playwright does: https://github.com/microsoft/playwright-vscode/blob/4fba0d0873ee9bf5de17219fa2f48201fd16162f/src/extension.ts#L403-L422. VS Code by default only shows failure messages from the last-ended test run, which was the original (unused) test run VS Code created, because it ends automatically after the runHandler's promise resolves. A good change to make, which happens to fix this bug, is ensuring results are retained in the order the user started the test runs versus the order in which they ended. Note that playwright is able to avoid the duplicate run since #213182, but I think this is still a sensible change to make. * fix other test --- .../contrib/testing/common/testResult.ts | 1 + .../contrib/testing/common/testResultService.ts | 17 ++++++++++++++--- .../test/common/testResultService.test.ts | 10 +++++++--- .../test/common/testResultStorage.test.ts | 1 + 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/testing/common/testResult.ts b/src/vs/workbench/contrib/testing/common/testResult.ts index 53353b58d98..ed3db492667 100644 --- a/src/vs/workbench/contrib/testing/common/testResult.ts +++ b/src/vs/workbench/contrib/testing/common/testResult.ts @@ -340,6 +340,7 @@ export class LiveTestResult extends Disposable implements ITestResult { public readonly id: string, public readonly persist: boolean, public readonly request: ResolvedTestRunRequest, + public readonly insertOrder: number, @ITelemetryService private readonly telemetry: ITelemetryService, ) { super(); diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index 8a836a39086..8790f01262f 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -78,6 +78,7 @@ export class TestResultService extends Disposable implements ITestResultService private _results: ITestResult[] = []; private readonly _resultsDisposables: DisposableStore[] = []; private testChangeEmitter = this._register(new Emitter()); + private insertOrderCounter = 0; /** * @inheritdoc @@ -139,7 +140,7 @@ export class TestResultService extends Disposable implements ITestResultService public createLiveResult(req: ResolvedTestRunRequest | ExtensionRunTestsRequest) { if ('targets' in req) { const id = generateUuid(); - return this.push(new LiveTestResult(id, true, req, this.telemetryService)); + return this.push(new LiveTestResult(id, true, req, this.insertOrderCounter++, this.telemetryService)); } let profile: ITestRunProfile | undefined; @@ -164,7 +165,7 @@ export class TestResultService extends Disposable implements ITestResultService }); } - return this.push(new LiveTestResult(req.id, req.persist, resolved, this.telemetryService)); + return this.push(new LiveTestResult(req.id, req.persist, resolved, this.insertOrderCounter++, this.telemetryService)); } /** @@ -251,7 +252,17 @@ export class TestResultService extends Disposable implements ITestResultService } private resort() { - this.results.sort((a, b) => (b.completedAt ?? Number.MAX_SAFE_INTEGER) - (a.completedAt ?? Number.MAX_SAFE_INTEGER)); + this.results.sort((a, b) => { + // Running tests should always be sorted higher: + if (!!a.completedAt !== !!b.completedAt) { + return a.completedAt === undefined ? -1 : 1; + } + + // Otherwise sort by insertion order, hydrated tests are always last: + const aComp = a instanceof LiveTestResult ? a.insertOrder : -1; + const bComp = b instanceof LiveTestResult ? b.insertOrder : -1; + return bComp - aComp; + }); } private updateIsRunning() { diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts index 38c0a2c83c2..ac0ba381e3b 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -40,13 +40,15 @@ suite('Workbench - Test Results Service', () => { }] }); + let insertCounter = 0; + class TestLiveTestResult extends LiveTestResult { constructor( id: string, persist: boolean, request: ResolvedTestRunRequest, ) { - super(id, persist, request, NullTelemetryService); + super(id, persist, request, insertCounter++, NullTelemetryService); ds.add(this); } @@ -263,6 +265,7 @@ suite('Workbench - Test Results Service', () => { '', false, defaultOpts([]), + insertCounter++, NullTelemetryService, )); results.clear(); @@ -270,12 +273,13 @@ suite('Workbench - Test Results Service', () => { assert.deepStrictEqual(results.results, [r2]); }); - test('keeps ongoing tests on top', async () => { + test('keeps ongoing tests on top, restored order when done', async () => { results.push(r); const r2 = results.push(new LiveTestResult( '', false, defaultOpts([]), + insertCounter++, NullTelemetryService, )); @@ -283,7 +287,7 @@ suite('Workbench - Test Results Service', () => { r2.markComplete(); assert.deepStrictEqual(results.results, [r, r2]); r.markComplete(); - assert.deepStrictEqual(results.results, [r, r2]); + assert.deepStrictEqual(results.results, [r2, r]); }); const makeHydrated = async (completedAt = 42, state = TestResultState.Passed) => new HydratedTestResult({ diff --git a/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts index 01218dbbe72..34c9fb80d0d 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts @@ -25,6 +25,7 @@ suite('Workbench - Test Result Storage', () => { '', true, { targets: [], group: TestRunProfileBitset.Run }, + 1, NullTelemetryService, )); From f771cf574394f0193189a5b7132157e2cd13e1bd Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 10 Dec 2024 13:38:27 -0700 Subject: [PATCH 082/479] Remove quota from sku messaging (#235753) * Remove quota from sku messaging * Update wording --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 2 +- .../welcomeGettingStarted/common/gettingStartedContent.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 9713d25a97f..ecd84a1ab9d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -866,7 +866,7 @@ class ChatSetupWelcomeContent extends Disposable { } // Limited SKU - const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}) with 2,000 code completions and 50 chat messages per month.", defaultChat.skusDocumentationUrl); + const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); const limitedSkuHeaderContainer = this.element.appendChild($('p')); limitedSkuHeaderContainer.appendChild(this._register(markdown.render(new MarkdownString(limitedSkuHeader, { isTrusted: true, supportThemeIcons: true }))).element); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 50e0e26bab2..637e77f71f8 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -211,7 +211,7 @@ export const startEntries: GettingStartedStartEntryContent = [ const Button = (title: string, href: string) => `[${title}](${href})`; const CopilotStepTitle = localize('gettingStarted.copilotSetup.title', "Use AI features with Copilot for free"); -const CopilotDescription = localize({ key: 'gettingStarted.copilotSetup.description', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "Write code faster and smarter using [Copilot]({0}). With your GitHub account, get 2,000 code completions and 50 chat messages per month for free.", product.defaultChatAgent?.documentationUrl ?? ''); +const CopilotDescription = localize({ key: 'gettingStarted.copilotSetup.description', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "Write code faster and smarter using [Copilot]({0}) for free with your GitHub account.", product.defaultChatAgent?.documentationUrl ?? ''); const CopilotTermsString = localize({ key: 'copilotTerms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to Copilot [Terms]({0}) and [Privacy Policy]({1}).", product.defaultChatAgent?.termsStatementUrl ?? '', product.defaultChatAgent?.privacyStatementUrl ?? ''); const CopilotSignedOutButton = Button(localize('setupCopilotButton.signIn', "Sign in to Use Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); const CopilotSignedInButton = Button(localize('setupCopilotButton.setup', "Set Up Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); From 253145591b4f998449ec2d6db4fe5c4c3ae0b30c Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Dec 2024 21:48:12 +0100 Subject: [PATCH 083/479] Git - export stash methods in the extension API (#235773) --- extensions/git/src/api/api1.ts | 12 ++++++++++++ extensions/git/src/api/git.d.ts | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 62e3182b515..a8a8fb694b1 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -272,6 +272,18 @@ export class ApiRepository implements Repository { mergeAbort(): Promise { return this.repository.mergeAbort(); } + + applyStash(index?: number): Promise { + return this.repository.applyStash(index); + } + + popStash(index?: number): Promise { + return this.repository.popStash(index); + } + + dropStash(index?: number): Promise { + return this.repository.dropStash(index); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index ee45e7a893a..851d5734977 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -266,6 +266,10 @@ export interface Repository { commit(message: string, opts?: CommitOptions): Promise; merge(ref: string): Promise; mergeAbort(): Promise; + + applyStash(index?: number): Promise; + popStash(index?: number): Promise; + dropStash(index?: number): Promise; } export interface RemoteSource { From 75e8699bf901d7ca9ddd882d045dfbe309470ee5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Dec 2024 15:10:10 -0800 Subject: [PATCH 084/479] Add extra type check in `findFiles2` (#235692) For https://github.com/microsoft/vscode-copilot-release/issues/2493 Looking at the code I'm still not sure how this can happen but it seems like we end up trying to call `.map` on a value that is not an array. Adding a more explicit exception here to hopefully track this down --- .../workbench/api/common/extHostWorkspace.ts | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 2f40bb8a392..b490bf7a242 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -468,7 +468,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac } // todo: consider exclude baseURI if available - return this._findFilesImpl(include, undefined, { + return this._findFilesImpl({ type: 'include', value: include }, { exclude: [excludeString], maxResults, useExcludeSettings: useFileExcludes ? ExcludeSettingOptions.FilesExclude : ExcludeSettingOptions.None, @@ -484,23 +484,26 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise { this._logService.trace(`extHostWorkspace#findFiles2New: fileSearch, extension: ${extensionId.value}, entryPoint: findFiles2New`); - return this._findFilesImpl(undefined, filePatterns, options, token); + return this._findFilesImpl({ type: 'filePatterns', value: filePatterns }, options, token); } private async _findFilesImpl( // the old `findFiles` used `include` to query, but the new `findFiles2` uses `filePattern` to query. // `filePattern` is the proper way to handle this, since it takes less precedence than the ignore files. - include: vscode.GlobPattern | undefined, - filePatterns: vscode.GlobPattern[] | undefined, + query: { type: 'include'; value: vscode.GlobPattern | undefined } | { type: 'filePatterns'; value: vscode.GlobPattern[] }, options: vscode.FindFiles2Options, - token: vscode.CancellationToken = CancellationToken.None): Promise { - if (token && token.isCancellationRequested) { + token: vscode.CancellationToken + ): Promise { + if (token.isCancellationRequested) { return Promise.resolve([]); } + const filePatternsToUse = query.type === 'include' ? [query.value] : query.value ?? []; + if (!Array.isArray(filePatternsToUse)) { + throw new Error(`Invalid file pattern provided ${filePatternsToUse}`); + } - const filePatternsToUse = include !== undefined ? [include] : filePatterns; - const queryOptions: QueryOptions[] = filePatternsToUse?.map(filePattern => { + const queryOptions: QueryOptions[] = filePatternsToUse.map(filePattern => { const excludePatterns = globsToISearchPatternBuilder(options.exclude); @@ -514,21 +517,22 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac maxResults: options.maxResults, excludePattern: excludePatterns.length > 0 ? excludePatterns : undefined, _reason: 'startFileSearch', - shouldGlobSearch: include ? undefined : true, + shouldGlobSearch: query.type === 'include' ? undefined : true, }; const parseInclude = parseSearchExcludeInclude(GlobPattern.from(filePattern)); const folderToUse = parseInclude?.folder; - if (include) { + if (query.type === 'include') { fileQueries.includePattern = parseInclude?.pattern; } else { fileQueries.filePattern = parseInclude?.pattern; } + return { folder: folderToUse, options: fileQueries }; - }) ?? []; + }); return this._findFilesBase(queryOptions, token); } From 5ece69fbf32e95c919af2ebeb8b00bb3b4c1eefc Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 11 Dec 2024 13:21:01 +0900 Subject: [PATCH 085/479] ci: fix flaky client module installation in legacy stage (#235786) --- build/.cachesalt | 2 +- .../product-build-linux-legacy-server.yml | 28 ++++--------------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/build/.cachesalt b/build/.cachesalt index 3c6029dc2f4..5a294f35396 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2024-09-04T10:21:29.952Z +2024-12-11T00:28:56.838Z diff --git a/build/azure-pipelines/linux/product-build-linux-legacy-server.yml b/build/azure-pipelines/linux/product-build-linux-legacy-server.yml index fcbdb3254f0..6e4022b4064 100644 --- a/build/azure-pipelines/linux/product-build-linux-legacy-server.yml +++ b/build/azure-pipelines/linux/product-build-linux-legacy-server.yml @@ -42,7 +42,9 @@ steps: libxkbfile-dev \ libkrb5-dev \ libgbm1 \ - rpm + rpm \ + gcc-10 \ + g++-10 sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb sudo chmod +x /etc/init.d/xvfb sudo update-rc.d xvfb defaults @@ -83,28 +85,6 @@ steps: imageName: vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) containerCommand: uname - - ${{ if or(eq(parameters.VSCODE_ARCH, 'arm64'), eq(parameters.VSCODE_ARCH, 'armhf')) }}: - - script: | - set -e - includes=$(cat << 'EOF' - { - "target_defaults": { - "conditions": [ - ["OS=='linux'", { - 'cflags_cc!': [ '-std=gnu++20' ], - 'cflags_cc': [ '-std=gnu++2a' ], - }] - ] - } - } - EOF - ) - if [ ! -d "$HOME/.gyp" ]; then - mkdir -p "$HOME/.gyp" - fi - echo "$includes" > "$HOME/.gyp/include.gypi" - displayName: Override gnu target for arm64 and arm - - script: | set -e @@ -123,6 +103,8 @@ steps: set -e export VSCODE_SYSROOT_PREFIX='-glibc-2.17' + export CC=$(which gcc-10) + export CXX=$(which g++-10) source ./build/azure-pipelines/linux/setup-env.sh --skip-sysroot for i in {1..5}; do # try 5 times From 9394d9716792e04a47f917b1760d5c4048170f7c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 10 Dec 2024 20:22:33 -0800 Subject: [PATCH 086/479] Remove event from MarkdownRenderer (#235046) Fixes #234539 `render` already supports passing in a callback so I don't think we need an event --- .../browser/markdownRenderer.ts | 19 +++++-------------- .../contrib/hover/browser/glyphHoverWidget.ts | 2 +- .../hover/browser/markdownHoverParticipant.ts | 12 +++++++----- .../browser/hintsWidget/hoverParticipant.ts | 14 +++++++------- .../browser/parameterHintsWidget.ts | 2 +- .../suggest/browser/suggestWidgetDetails.ts | 11 ++++++----- .../chatConfirmationWidget.ts | 2 +- .../contrib/chat/browser/chatListRenderer.ts | 2 +- .../contrib/chat/browser/chatSetup.ts | 2 +- .../viewsWelcome/chatViewWelcomeController.ts | 2 +- .../comments/browser/commentThreadBody.ts | 2 +- .../preferences/browser/settingsTree.ts | 2 +- .../contrib/scm/browser/scmViewPane.ts | 2 +- .../testResultsView/testResultsOutput.ts | 2 +- .../browser/gettingStarted.ts | 1 - 15 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts b/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts index 61ece2ec8e1..4bd0ef15ad0 100644 --- a/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts +++ b/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts @@ -6,17 +6,16 @@ import { MarkdownRenderOptions, MarkedOptions, renderMarkdown } from '../../../../../base/browser/markdownRenderer.js'; import { createTrustedTypesPolicy } from '../../../../../base/browser/trustedTypes.js'; import { onUnexpectedError } from '../../../../../base/common/errors.js'; -import { Emitter } from '../../../../../base/common/event.js'; import { IMarkdownString, MarkdownStringTrustedOptions } from '../../../../../base/common/htmlContent.js'; import { DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; -import './renderedMarkdown.css'; -import { applyFontInfo } from '../../../config/domFontInfo.js'; -import { ICodeEditor } from '../../../editorBrowser.js'; +import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { ILanguageService } from '../../../../common/languages/language.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../common/languages/modesRegistry.js'; import { tokenizeToString } from '../../../../common/languages/textToHtmlTokenizer.js'; -import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; +import { applyFontInfo } from '../../../config/domFontInfo.js'; +import { ICodeEditor } from '../../../editorBrowser.js'; +import './renderedMarkdown.css'; export interface IMarkdownRenderResult extends IDisposable { readonly element: HTMLElement; @@ -32,7 +31,7 @@ export interface IMarkdownRendererOptions { * Markdown renderer that can render codeblocks with the editor mechanics. This * renderer should always be preferred. */ -export class MarkdownRenderer implements IDisposable { +export class MarkdownRenderer { private static _ttpTokenizer = createTrustedTypesPolicy('tokenizeToString', { createHTML(html: string) { @@ -40,19 +39,12 @@ export class MarkdownRenderer implements IDisposable { } }); - private readonly _onDidRenderAsync = new Emitter(); - readonly onDidRenderAsync = this._onDidRenderAsync.event; - constructor( private readonly _options: IMarkdownRendererOptions, @ILanguageService private readonly _languageService: ILanguageService, @IOpenerService private readonly _openerService: IOpenerService, ) { } - dispose(): void { - this._onDidRenderAsync.dispose(); - } - render(markdown: IMarkdownString | undefined, options?: MarkdownRenderOptions, markedOptions?: MarkedOptions): IMarkdownRenderResult { if (!markdown) { const element = document.createElement('span'); @@ -103,7 +95,6 @@ export class MarkdownRenderer implements IDisposable { return element; }, - asyncRenderCallback: () => this._onDidRenderAsync.fire(), actionHandler: { callback: (link) => this.openMarkdownLink(link, markdown), disposables: disposables diff --git a/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts b/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts index f5b8fbb2fc5..a6a120b040a 100644 --- a/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts @@ -48,7 +48,7 @@ export class GlyphHoverWidget extends Disposable implements IOverlayWidget, IHov this._hover = this._register(new HoverWidget(true)); this._hover.containerDomNode.classList.toggle('hidden', !this._isVisible); - this._markdownRenderer = this._register(new MarkdownRenderer({ editor: this._editor }, languageService, openerService)); + this._markdownRenderer = new MarkdownRenderer({ editor: this._editor }, languageService, openerService); this._hoverOperation = this._register(new HoverOperation(this._editor, new GlyphHoverComputer(this._editor))); this._register(this._hoverOperation.onResult((result) => this._withResult(result))); diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index 7e01b664105..481e7791355 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -489,12 +489,14 @@ function renderMarkdownInContainer( } const markdownHoverElement = $('div.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents')); - const renderer = disposables.add(new MarkdownRenderer({ editor }, languageService, openerService)); - disposables.add(renderer.onDidRenderAsync(() => { - hoverContentsElement.className = 'hover-contents code-hover-contents'; - onFinishedRendering(); + const renderer = new MarkdownRenderer({ editor }, languageService, openerService); + + const renderedContents = disposables.add(renderer.render(markdownString, { + asyncRenderCallback: () => { + hoverContentsElement.className = 'hover-contents code-hover-contents'; + onFinishedRendering(); + } })); - const renderedContents = disposables.add(renderer.render(markdownString)); hoverContentsElement.appendChild(renderedContents.element); renderedMarkdownContents.appendChild(markdownHoverElement); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts index fefa9400dfc..66d3c143c01 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts @@ -138,15 +138,15 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan const $ = dom.$; const markdownHoverElement = $('div.hover-row.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents', { ['aria-live']: 'assertive' })); - const renderer = disposables.add(new MarkdownRenderer({ editor: this._editor }, this._languageService, this._openerService)); + const renderer = new MarkdownRenderer({ editor: this._editor }, this._languageService, this._openerService); const render = (code: string) => { - disposables.add(renderer.onDidRenderAsync(() => { - hoverContentsElement.className = 'hover-contents code-hover-contents'; - context.onContentsChanged(); - })); - const inlineSuggestionAvailable = nls.localize('inlineSuggestionFollows', "Suggestion:"); - const renderedContents = disposables.add(renderer.render(new MarkdownString().appendText(inlineSuggestionAvailable).appendCodeblock('text', code))); + const renderedContents = disposables.add(renderer.render(new MarkdownString().appendText(inlineSuggestionAvailable).appendCodeblock('text', code), { + asyncRenderCallback: () => { + hoverContentsElement.className = 'hover-contents code-hover-contents'; + context.onContentsChanged(); + } + })); hoverContentsElement.replaceChildren(renderedContents.element); }; diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index b2f044f2f5c..67334742dad 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -67,7 +67,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { ) { super(); - this.markdownRenderer = this._register(new MarkdownRenderer({ editor }, languageService, openerService)); + this.markdownRenderer = new MarkdownRenderer({ editor }, languageService, openerService); this.keyVisible = Context.Visible.bindTo(contextKeyService); this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService); diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts index 22f29a981c5..ac2af6022b0 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts @@ -174,13 +174,14 @@ export class SuggestDetailsWidget { } else if (documentation) { this._docs.classList.add('markdown-docs'); dom.clearNode(this._docs); - const renderedContents = this._markdownRenderer.render(documentation); + const renderedContents = this._markdownRenderer.render(documentation, { + asyncRenderCallback: () => { + this.layout(this._size.width, this._type.clientHeight + this._docs.clientHeight); + this._onDidChangeContents.fire(this); + } + }); this._docs.appendChild(renderedContents.element); this._renderDisposeable.add(renderedContents); - this._renderDisposeable.add(this._markdownRenderer.onDidRenderAsync(() => { - this.layout(this._size.width, this._type.clientHeight + this._docs.clientHeight); - this._onDidChangeContents.fire(this); - })); } this.domNode.style.userSelect = 'text'; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts index 44fd960a147..df2b54b53e1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts @@ -46,7 +46,7 @@ export class ChatConfirmationWidget extends Disposable { dom.h('.chat-confirmation-buttons-container@buttonsContainer'), ]); this._domNode = elements.root; - const renderer = this._register(this.instantiationService.createInstance(MarkdownRenderer, {})); + const renderer = this.instantiationService.createInstance(MarkdownRenderer, {}); const renderedTitle = this._register(renderer.render(new MarkdownString(title))); elements.title.appendChild(renderedTitle.element); diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 527aba95f06..2bc16470629 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -157,7 +157,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer extends D this.commentService.setActiveEditingCommentThread(this._commentThread); })); - this._markdownRenderer = this._register(new MarkdownRenderer(this._options, this.languageService, this.openerService)); + this._markdownRenderer = new MarkdownRenderer(this._options, this.languageService, this.openerService); } focus(commentUniqueId?: number) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 7a1f0adcdaa..eb600aa17e9 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -841,7 +841,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre ) { super(); - this.markdownRenderer = this._register(_instantiationService.createInstance(MarkdownRenderer, {})); + this.markdownRenderer = _instantiationService.createInstance(MarkdownRenderer, {}); this.ignoredSettings = getIgnoredSettings(getDefaultIgnoredSettings(), this._configService); this._register(this._configService.onDidChangeConfiguration(e => { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 4ee1c3aa7bf..fd25da9f13c 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1957,7 +1957,7 @@ class SCMInputWidget { this.contextViewService.hideContextView(); })); - const renderer = disposables.add(this.instantiationService.createInstance(MarkdownRenderer, {})); + const renderer = this.instantiationService.createInstance(MarkdownRenderer, {}); const renderedMarkdown = renderer.render(message, { actionHandler: { callback: (link) => { diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts index ac9eefb824b..4f06c5ee9ca 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts @@ -206,7 +206,7 @@ export class DiffContentProvider extends Disposable implements IPeekOutputRender export class MarkdownTestMessagePeek extends Disposable implements IPeekOutputRenderer { private readonly markdown = new Lazy( - () => this._register(this.instantiationService.createInstance(MarkdownRenderer, {})), + () => this.instantiationService.createInstance(MarkdownRenderer, {}), ); private readonly rendered = this._register(new DisposableStore()); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index cb25dc9d925..b0f5b5f009b 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -1511,7 +1511,6 @@ export class GettingStartedPage extends EditorPane { "{0} collects usage data. Read our {1} and learn how to {2}.", this.productService.nameShort, privacyStatementButton, optOutButton); parent.append(mdRenderer.render({ value: text, isTrusted: true }).element); - mdRenderer.dispose(); } private getKeybindingLabel(command: string) { From 4cb01939683a850cf023948a8d6e430ef53b361d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 11 Dec 2024 08:21:05 +0100 Subject: [PATCH 087/479] Context menu on simple text editor does not have the standard options (fix #191628) (#235791) --- src/vs/editor/browser/editorExtensions.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index c8453d78c1f..3751b26de7d 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -652,6 +652,11 @@ export const UndoCommand = registerCommand(new MultiCommand({ group: '', title: nls.localize('undo', "Undo"), order: 1 + }, { + menuId: MenuId.SimpleEditorContext, + group: '1_do', + title: nls.localize('undo', "Undo"), + order: 1 }] })); @@ -676,6 +681,11 @@ export const RedoCommand = registerCommand(new MultiCommand({ group: '', title: nls.localize('redo', "Redo"), order: 1 + }, { + menuId: MenuId.SimpleEditorContext, + group: '1_do', + title: nls.localize('redo', "Redo"), + order: 2 }] })); @@ -699,5 +709,10 @@ export const SelectAllCommand = registerCommand(new MultiCommand({ group: '', title: nls.localize('selectAll', "Select All"), order: 1 + }, { + menuId: MenuId.SimpleEditorContext, + group: '9_select', + title: nls.localize('selectAll', "Select All"), + order: 1 }] })); From 679a9f1aceeb8b1aa3bcd0542eb5e5aca6046600 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 11 Dec 2024 09:19:48 +0100 Subject: [PATCH 088/479] :up: `@parcel/watcher@2.5.0` (#235710) --- build/npm/postinstall.js | 21 +- extensions/package-lock.json | 325 +++++++++++++++++- extensions/package.json | 7 +- package-lock.json | 310 ++++++++++++++++- package.json | 5 +- remote/package-lock.json | 310 ++++++++++++++++- remote/package.json | 5 +- .../files/test/node/parcelWatcher.test.ts | 4 +- 8 files changed, 921 insertions(+), 66 deletions(-) diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 88e3c9eb1bb..c1f22aa5002 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -62,6 +62,7 @@ function npmInstall(dir, opts) { log(dir, 'Installing dependencies...'); run(npm, command.split(' '), opts); } + removeParcelWatcherPrebuild(dir); } function setNpmrcConfig(dir, env) { @@ -96,11 +97,27 @@ function setNpmrcConfig(dir, env) { } } +function removeParcelWatcherPrebuild(dir) { + const parcelModuleFolder = path.join(root, dir, 'node_modules', '@parcel'); + if (!fs.existsSync(parcelModuleFolder)) { + return; + } + + const parcelModules = fs.readdirSync(parcelModuleFolder); + for (const moduleName of parcelModules) { + if (moduleName.startsWith('watcher-')) { + const modulePath = path.join(parcelModuleFolder, moduleName); + fs.rmSync(modulePath, { recursive: true, force: true }); + log(dir, `Removed @parcel/watcher prebuilt module ${modulePath}`); + } + } +} + for (let dir of dirs) { if (dir === '') { - // already executed in root - continue; + removeParcelWatcherPrebuild(dir); + continue; // already executed in root } let opts; diff --git a/extensions/package-lock.json b/extensions/package-lock.json index 765f26586a3..04f0bfcde5c 100644 --- a/extensions/package-lock.json +++ b/extensions/package-lock.json @@ -13,7 +13,7 @@ "typescript": "^5.7.2" }, "devDependencies": { - "@parcel/watcher": "2.1.0", + "@parcel/watcher": "2.5.0", "esbuild": "0.23.0", "vscode-grammar-updater": "^1.1.0" } @@ -403,17 +403,306 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.1.0.tgz", - "integrity": "sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { + "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 10.0.0" }, @@ -459,6 +748,19 @@ "node": ">=10.13" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/esbuild": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", @@ -570,17 +872,6 @@ "node": "^16 || ^18 || >= 20" } }, - "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", - "dev": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", diff --git a/extensions/package.json b/extensions/package.json index 1d189197e74..e70678990af 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -10,14 +10,11 @@ "postinstall": "node ./postinstall.mjs" }, "devDependencies": { - "@parcel/watcher": "2.1.0", + "@parcel/watcher": "2.5.0", "esbuild": "0.23.0", "vscode-grammar-updater": "^1.1.0" }, "overrides": { - "node-gyp-build": "4.8.1", - "@parcel/watcher@2.1.0": { - "node-addon-api": "7.1.0" - } + "node-gyp-build": "4.8.1" } } diff --git a/package-lock.json b/package-lock.json index a16b3ecf06f..e659e5506fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", - "@parcel/watcher": "2.1.0", + "@parcel/watcher": "2.5.0", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", @@ -1727,24 +1727,312 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.1.0.tgz", - "integrity": "sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "hasInstallScript": true, + "license": "MIT", "dependencies": { + "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" + "node-addon-api": "^7.0.0" }, "engines": { "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -12351,16 +12639,6 @@ } } }, - "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/node-html-markdown": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/node-html-markdown/-/node-html-markdown-1.3.0.tgz", diff --git a/package.json b/package.json index f283587b8c5..12ff0ff2f0b 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "dependencies": { "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", - "@parcel/watcher": "2.1.0", + "@parcel/watcher": "2.5.0", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", @@ -224,9 +224,6 @@ "node-gyp-build": "4.8.1", "kerberos@2.1.1": { "node-addon-api": "7.1.0" - }, - "@parcel/watcher@2.1.0": { - "node-addon-api": "7.1.0" } }, "repository": { diff --git a/remote/package-lock.json b/remote/package-lock.json index f0ed1ccef11..dfbff101a88 100644 --- a/remote/package-lock.json +++ b/remote/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", - "@parcel/watcher": "2.1.0", + "@parcel/watcher": "2.5.0", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/proxy-agent": "^0.27.0", @@ -88,16 +88,232 @@ "integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==" }, "node_modules/@parcel/watcher": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.1.0.tgz", - "integrity": "sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", "hasInstallScript": true, + "license": "MIT", "dependencies": { + "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">= 10.0.0" }, @@ -106,6 +322,78 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/@tootallnate/once": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-3.0.0.tgz", @@ -797,16 +1085,6 @@ "node": "^16 || ^18 || >= 20" } }, - "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/node-pty": { "version": "1.1.0-beta22", "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta22.tgz", diff --git a/remote/package.json b/remote/package.json index 33afee907a6..b5ebb761e09 100644 --- a/remote/package.json +++ b/remote/package.json @@ -5,7 +5,7 @@ "dependencies": { "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", - "@parcel/watcher": "2.1.0", + "@parcel/watcher": "2.5.0", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/proxy-agent": "^0.27.0", @@ -43,9 +43,6 @@ "node-gyp-build": "4.8.1", "kerberos@2.1.1": { "node-addon-api": "7.1.0" - }, - "@parcel/watcher@2.1.0": { - "node-addon-api": "7.1.0" } } } diff --git a/src/vs/platform/files/test/node/parcelWatcher.test.ts b/src/vs/platform/files/test/node/parcelWatcher.test.ts index 7a9533e0d8a..558b81e19a9 100644 --- a/src/vs/platform/files/test/node/parcelWatcher.test.ts +++ b/src/vs/platform/files/test/node/parcelWatcher.test.ts @@ -745,7 +745,7 @@ suite.skip('File Watcher (parcel)', function () { assert.strictEqual(instance.failed, true); }); - test('watch requests support suspend/resume (folder, does not exist in beginning, not reusing watcher)', async () => { + (isWindows /* Windows: times out for some reason */ ? test.skip : test)('watch requests support suspend/resume (folder, does not exist in beginning, not reusing watcher)', async () => { await testWatchFolderDoesNotExist(false); }); @@ -800,7 +800,7 @@ suite.skip('File Watcher (parcel)', function () { await basicCrudTest(filePath); } - test('watch requests support suspend/resume (folder, exist in beginning, not reusing watcher)', async () => { + (isWindows /* Windows: times out for some reason */ ? test.skip : test)('watch requests support suspend/resume (folder, exist in beginning, not reusing watcher)', async () => { await testWatchFolderExists(false); }); From ae176274ac92eb4c0bfb93343eca18e4ebb46ea7 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 11 Dec 2024 09:49:45 +0100 Subject: [PATCH 089/479] Increasing the default accessibility page size to 500 (#235798) increasing the default accessibility page size to 500 --- 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 a1c42699069..3fca8a3fc7b 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -5553,7 +5553,7 @@ export const EditorOptions = { } )), accessibilitySupport: register(new EditorAccessibilitySupport()), - accessibilityPageSize: register(new EditorIntOption(EditorOption.accessibilityPageSize, 'accessibilityPageSize', 10, 1, Constants.MAX_SAFE_SMALL_INTEGER, + accessibilityPageSize: register(new EditorIntOption(EditorOption.accessibilityPageSize, 'accessibilityPageSize', 500, 1, Constants.MAX_SAFE_SMALL_INTEGER, { description: nls.localize('accessibilityPageSize', "Controls the number of lines in the editor that can be read out by a screen reader at once. When we detect a screen reader we automatically set the default to be 500. Warning: this has a performance implication for numbers larger than the default."), tags: ['accessibility'] From 4ccaaa8dfd5fff29c8e6b01926601a2558006443 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 10:53:16 +0100 Subject: [PATCH 090/479] Add ~ escape hatch to simple file picker (#235802) Fixes #122197 --- .../services/dialogs/browser/simpleFileDialog.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 0fc6fad2c5a..11b428f7f8e 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -422,7 +422,7 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { // onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything if (this.isValueChangeFromUser()) { // If the user has just entered more bad path, don't change anything - if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) { + if (!equalsIgnoreCase(value, this.constructFullUserPath()) && (!this.isBadSubpath(value) || this.canTildaEscapeHatch(value))) { this.filePickBox.validationMessage = undefined; const filePickBoxUri = this.filePickBoxValue(); let updated: UpdateResult = UpdateResult.NotUpdated; @@ -555,10 +555,16 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { return dir; } + private canTildaEscapeHatch(value: string): boolean { + return !!(value.endsWith('~') && this.isBadSubpath(value)); + } + private tildaReplace(value: string): URI { const home = this.trueHome; if ((value.length > 0) && (value[0] === '~')) { return resources.joinPath(home, value.substring(1)); + } else if (this.canTildaEscapeHatch(value)) { + return home; } return this.remoteUriFrom(value); } @@ -574,7 +580,7 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { } private async tryUpdateItems(value: string, valueUri: URI): Promise { - if ((value.length > 0) && (value[0] === '~')) { + if ((value.length > 0) && ((value[0] === '~') || this.canTildaEscapeHatch(value))) { const newDir = this.tildaReplace(value); return await this.updateItems(newDir, true) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated; } else if (value === '\\') { @@ -598,7 +604,7 @@ export class SimpleFileDialog extends Disposable implements ISimpleFileDialog { return await this.updateItems(valueUri) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated; } else if (this.endsWithSlash(value)) { // The input box contains a path that doesn't exist on the system. - this.filePickBox.validationMessage = nls.localize('remoteFileDialog.badPath', 'The path does not exist.'); + this.filePickBox.validationMessage = nls.localize('remoteFileDialog.badPath', 'The path does not exist. Use ~ to go to your home directory.'); // Save this bad path. It can take too long to a stat on every user entered character, but once a user enters a bad path they are likely // to keep typing more bad path. We can compare against this bad path and see if the user entered path starts with it. this.badPath = value; From cc746fd86fe4887cfa38b1a4589bf56f3e46f557 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 11 Dec 2024 11:04:00 +0100 Subject: [PATCH 091/479] sticky scroll: Fixing folding icon placement issue (#235804) fixing folding icon placement issue --- src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css | 2 ++ .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 276082256f6..ffe968fdb57 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -37,6 +37,8 @@ .monaco-editor .sticky-line-number .codicon-folding-collapsed { float: right; transition: var(--vscode-editorStickyScroll-foldingOpacityTransition); + display: flex; + align-items: center; } .monaco-editor .sticky-line-content { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 8dff7940199..3d1d583cb28 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -333,7 +333,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } this._editor.applyFontInfo(lineHTMLNode); - this._editor.applyFontInfo(innerLineNumberHTML); + this._editor.applyFontInfo(lineNumberHTMLNode); lineNumberHTMLNode.style.lineHeight = `${this._lineHeight}px`; From 74a879d760ff188575b2216ff634261f0f40ddd4 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 11:13:01 +0100 Subject: [PATCH 092/479] Should ports status bar entry only show up when ports are or have been used (#235805) Fixes #231402 --- .../contrib/remote/browser/remoteExplorer.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index ef741a340d3..480feea12ad 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -44,6 +44,7 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu private readonly contextKeyListener = this._register(new MutableDisposable()); private readonly activityBadge = this._register(new MutableDisposable()); private entryAccessor: IStatusbarEntryAccessor | undefined; + private hasPortsInSession: boolean = false; constructor( @IContextKeyService private readonly contextKeyService: IContextKeyService, @@ -60,6 +61,11 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu })); this.enableBadgeAndStatusBar(); this.enableForwardedPortsFeatures(); + if (!this.environmentService.remoteAuthority) { + this._register(Event.once(this.tunnelService.onTunnelOpened)(() => { + this.hasPortsInSession = true; + })); + } } private async getViewContainer(): Promise { @@ -131,6 +137,11 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu } private updateStatusBar() { + if (!this.environmentService.remoteAuthority && !this.hasPortsInSession) { + // We only want to show the ports status bar entry when the user has taken an action that indicates that they might care about it. + return; + } + if (!this.entryAccessor) { this._register(this.entryAccessor = this.statusbarService.addEntry(this.entry, 'status.forwardedPorts', StatusbarAlignment.LEFT, 40)); } else { From a97e00ea3b47c1062048baba4deb26d4fcc17600 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 11 Dec 2024 11:16:38 +0100 Subject: [PATCH 093/479] Adding action to hide the hover (#235806) adding action to hide the hover --- .../contrib/hover/browser/hoverActionIds.ts | 1 + .../contrib/hover/browser/hoverActions.ts | 21 ++++++++++++++++++- .../hover/browser/hoverContribution.ts | 3 ++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/hoverActionIds.ts b/src/vs/editor/contrib/hover/browser/hoverActionIds.ts index 95cd48dda49..75b3930c791 100644 --- a/src/vs/editor/contrib/hover/browser/hoverActionIds.ts +++ b/src/vs/editor/contrib/hover/browser/hoverActionIds.ts @@ -6,6 +6,7 @@ import * as nls from '../../../../nls.js'; export const SHOW_OR_FOCUS_HOVER_ACTION_ID = 'editor.action.showHover'; export const SHOW_DEFINITION_PREVIEW_HOVER_ACTION_ID = 'editor.action.showDefinitionPreviewHover'; +export const HIDE_HOVER_ACTION_ID = 'editor.action.hideHover'; export const SCROLL_UP_HOVER_ACTION_ID = 'editor.action.scrollUpHover'; export const SCROLL_DOWN_HOVER_ACTION_ID = 'editor.action.scrollDownHover'; export const SCROLL_LEFT_HOVER_ACTION_ID = 'editor.action.scrollLeftHover'; diff --git a/src/vs/editor/contrib/hover/browser/hoverActions.ts b/src/vs/editor/contrib/hover/browser/hoverActions.ts index 8368eae643f..a2f16bc0c4c 100644 --- a/src/vs/editor/contrib/hover/browser/hoverActions.ts +++ b/src/vs/editor/contrib/hover/browser/hoverActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DECREASE_HOVER_VERBOSITY_ACTION_ID, DECREASE_HOVER_VERBOSITY_ACTION_LABEL, GO_TO_BOTTOM_HOVER_ACTION_ID, GO_TO_TOP_HOVER_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_LABEL, PAGE_DOWN_HOVER_ACTION_ID, PAGE_UP_HOVER_ACTION_ID, SCROLL_DOWN_HOVER_ACTION_ID, SCROLL_LEFT_HOVER_ACTION_ID, SCROLL_RIGHT_HOVER_ACTION_ID, SCROLL_UP_HOVER_ACTION_ID, SHOW_DEFINITION_PREVIEW_HOVER_ACTION_ID, SHOW_OR_FOCUS_HOVER_ACTION_ID } from './hoverActionIds.js'; +import { DECREASE_HOVER_VERBOSITY_ACTION_ID, DECREASE_HOVER_VERBOSITY_ACTION_LABEL, GO_TO_BOTTOM_HOVER_ACTION_ID, GO_TO_TOP_HOVER_ACTION_ID, HIDE_HOVER_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_LABEL, PAGE_DOWN_HOVER_ACTION_ID, PAGE_UP_HOVER_ACTION_ID, SCROLL_DOWN_HOVER_ACTION_ID, SCROLL_LEFT_HOVER_ACTION_ID, SCROLL_RIGHT_HOVER_ACTION_ID, SCROLL_UP_HOVER_ACTION_ID, SHOW_DEFINITION_PREVIEW_HOVER_ACTION_ID, SHOW_OR_FOCUS_HOVER_ACTION_ID } from './hoverActionIds.js'; import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; @@ -149,6 +149,25 @@ export class ShowDefinitionPreviewHoverAction extends EditorAction { } } +export class HideContentHoverAction extends EditorAction { + + constructor() { + super({ + id: HIDE_HOVER_ACTION_ID, + label: nls.localize2({ + key: 'hideHover', + comment: ['Label for action that will hide the hover in the editor.'] + }, "Hide Hover"), + alias: 'Hide Content Hover', + precondition: undefined + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + ContentHoverController.get(editor)?.hideContentHover(); + } +} + export class ScrollUpHoverAction extends EditorAction { constructor() { diff --git a/src/vs/editor/contrib/hover/browser/hoverContribution.ts b/src/vs/editor/contrib/hover/browser/hoverContribution.ts index 0143aa6ac24..9e4168a1be0 100644 --- a/src/vs/editor/contrib/hover/browser/hoverContribution.ts +++ b/src/vs/editor/contrib/hover/browser/hoverContribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DecreaseHoverVerbosityLevel, GoToBottomHoverAction, GoToTopHoverAction, IncreaseHoverVerbosityLevel, PageDownHoverAction, PageUpHoverAction, ScrollDownHoverAction, ScrollLeftHoverAction, ScrollRightHoverAction, ScrollUpHoverAction, ShowDefinitionPreviewHoverAction, ShowOrFocusHoverAction } from './hoverActions.js'; +import { DecreaseHoverVerbosityLevel, GoToBottomHoverAction, GoToTopHoverAction, HideContentHoverAction, IncreaseHoverVerbosityLevel, PageDownHoverAction, PageUpHoverAction, ScrollDownHoverAction, ScrollLeftHoverAction, ScrollRightHoverAction, ScrollUpHoverAction, ShowDefinitionPreviewHoverAction, ShowOrFocusHoverAction } from './hoverActions.js'; import { EditorContributionInstantiation, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js'; import { editorHoverBorder } from '../../../../platform/theme/common/colorRegistry.js'; import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; @@ -20,6 +20,7 @@ registerEditorContribution(ContentHoverController.ID, ContentHoverController, Ed registerEditorContribution(GlyphHoverController.ID, GlyphHoverController, EditorContributionInstantiation.BeforeFirstInteraction); registerEditorAction(ShowOrFocusHoverAction); registerEditorAction(ShowDefinitionPreviewHoverAction); +registerEditorAction(HideContentHoverAction); registerEditorAction(ScrollUpHoverAction); registerEditorAction(ScrollDownHoverAction); registerEditorAction(ScrollLeftHoverAction); From 1b4662bc51d7ec7e89a6e1a228e5ac140e253c20 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 11:22:47 +0100 Subject: [PATCH 094/479] a11y view is blank after running `focus comment on line` (#235807) Fixes #229857 --- .../contrib/comments/browser/commentThreadZoneWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 6790337c79a..eb2a0396eee 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -188,7 +188,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget public reveal(commentUniqueId?: number, focus: CommentWidgetFocus = CommentWidgetFocus.None) { this.makeVisible(commentUniqueId, focus); - const comment = this._commentThread.comments?.find(comment => comment.uniqueIdInThread === commentUniqueId); + const comment = this._commentThread.comments?.find(comment => comment.uniqueIdInThread === commentUniqueId) ?? this._commentThread.comments?.[0]; this.commentService.setActiveCommentAndThread(this.uniqueOwner, { thread: this._commentThread, comment }); } From a40e594f25e47cf52621ae0914376636372d69f0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Dec 2024 11:26:59 +0100 Subject: [PATCH 095/479] Check for cyclic dependencies during compile (#235808) * Check for cyclic dependencies during compile Changes gulp-tsb to check the emitted JS code for cyclic dependencies. Historically we never cared about cycles between TS files as long as they dissappeared after compile (e.g type-dependencies, not runtime dependencies) https://github.com/microsoft/vscode-internalbacklog/issues/5271 * fix cycling dependencies fyi @aeschli @aiday-mar * remove cyclic dependency with unused `BasedTextEdit` fyi @hediet * remove cycle between chatEditService and chatEditingSession fyi @alexdima * remove cyclic dependency between chatSetup and chatViewPane fyi @roblourens * better cycle detection * don't check cycles when not needed * clear graph when reprocessing file dependencies * remove cycle between with `notebookChatEditController` fyi @DonJayamanne * modernize and cleanup tsb/utils --- build/lib/tsb/builder.js | 64 ++++++- build/lib/tsb/builder.ts | 78 +++++++- build/lib/tsb/utils.js | 155 +++++++--------- build/lib/tsb/utils.ts | 171 ++++++++---------- src/vs/editor/common/core/textEdit.ts | 14 -- .../hoverColorPicker/hoverColorPicker.ts | 15 ++ .../hoverColorPickerContribution.ts | 11 +- .../hover/browser/contentHoverController.ts | 2 +- .../browser/actions/chatCodeblockActions.ts | 37 +++- .../browser/chatEditing/chatEditingService.ts | 10 +- .../browser/chatEditing/chatEditingSession.ts | 5 +- .../contrib/chat/browser/chatEditorActions.ts | 2 +- .../contrib/chat/browser/chatSetup.ts | 21 +-- .../contrib/chat/browser/chatViewPane.ts | 23 ++- .../contrib/chat/browser/codeBlockPart.ts | 36 +--- .../contrib/chat/common/chatEditingService.ts | 7 + .../chatEdit/notebookChatEditContext.ts | 9 + .../chatEdit/notebookChatEditController.ts | 6 +- 18 files changed, 358 insertions(+), 308 deletions(-) create mode 100644 src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPicker.ts create mode 100644 src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts diff --git a/build/lib/tsb/builder.js b/build/lib/tsb/builder.js index cee2758e896..e7a2519d1c9 100644 --- a/build/lib/tsb/builder.js +++ b/build/lib/tsb/builder.js @@ -26,6 +26,8 @@ function normalize(path) { function createTypeScriptBuilder(config, projectFile, cmd) { const _log = config.logFn; const host = new LanguageServiceHost(cmd, projectFile, _log); + const outHost = new LanguageServiceHost({ ...cmd, options: { ...cmd.options, sourceRoot: cmd.options.outDir } }, cmd.options.outDir ?? '', _log); + let lastCycleCheckVersion; const service = ts.createLanguageService(host, ts.createDocumentRegistry()); const lastBuildVersion = Object.create(null); const lastDtsHash = Object.create(null); @@ -251,6 +253,11 @@ function createTypeScriptBuilder(config, projectFile, cmd) { lastDtsHash[fileName] = value.signature; filesWithChangedSignature.push(fileName); } + // line up for cycle check + const jsValue = value.files.find(candidate => candidate.basename.endsWith('.js')); + if (jsValue) { + outHost.addScriptSnapshot(jsValue.path, new ScriptSnapshot(String(jsValue.contents), new Date())); + } }).catch(e => { // can't just skip this or make a result up.. host.error(`ERROR emitting ${fileName}`); @@ -341,16 +348,37 @@ function createTypeScriptBuilder(config, projectFile, cmd) { }); } workOnNext(); + }).then(() => { + // check for cyclic dependencies + const thisCycleCheckVersion = outHost.getProjectVersion(); + if (thisCycleCheckVersion === lastCycleCheckVersion) { + return; + } + const oneCycle = outHost.hasCyclicDependency(); + lastCycleCheckVersion = thisCycleCheckVersion; + delete oldErrors[projectFile]; + if (oneCycle) { + const cycleError = { + category: ts.DiagnosticCategory.Error, + code: 1, + file: undefined, + start: undefined, + length: undefined, + messageText: `CYCLIC dependency between ${oneCycle}` + }; + onError(cycleError); + newErrors[projectFile] = [cycleError]; + } }).then(() => { // store the build versions to not rebuilt the next time newLastBuildVersion.forEach((value, key) => { lastBuildVersion[key] = value; }); // print old errors and keep them - utils.collections.forEach(oldErrors, entry => { - entry.value.forEach(diag => onError(diag)); - newErrors[entry.key] = entry.value; - }); + for (const [key, value] of Object.entries(oldErrors)) { + value.forEach(diag => onError(diag)); + newErrors[key] = value; + } oldErrors = newErrors; // print stats const headNow = process.memoryUsage().heapUsed; @@ -415,7 +443,7 @@ class LanguageServiceHost { this._snapshots = Object.create(null); this._filesInProject = new Set(_cmdLine.fileNames); this._filesAdded = new Set(); - this._dependencies = new utils.graph.Graph(s => s); + this._dependencies = new utils.graph.Graph(); this._dependenciesRecomputeList = []; this._fileNameToDeclaredModule = Object.create(null); this._projectVersion = 1; @@ -478,10 +506,6 @@ class LanguageServiceHost { } if (!old || old.getVersion() !== snapshot.getVersion()) { this._dependenciesRecomputeList.push(filename); - const node = this._dependencies.lookup(filename); - if (node) { - node.outgoing = Object.create(null); - } // (cheap) check for declare module LanguageServiceHost._declareModule.lastIndex = 0; let match; @@ -523,9 +547,19 @@ class LanguageServiceHost { filename = normalize(filename); const node = this._dependencies.lookup(filename); if (node) { - utils.collections.forEach(node.incoming, entry => target.push(entry.key)); + node.incoming.forEach(entry => target.push(entry.data)); } } + hasCyclicDependency() { + // Ensure dependencies are up to date + while (this._dependenciesRecomputeList.length) { + this._processFile(this._dependenciesRecomputeList.pop()); + } + const cycle = this._dependencies.findCycle(); + return cycle + ? cycle.join(' -> ') + : undefined; + } _processFile(filename) { if (filename.match(/.*\.d\.ts$/)) { return; @@ -537,6 +571,8 @@ class LanguageServiceHost { return; } const info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true); + // (0) clear out old dependencies + this._dependencies.resetNode(filename); // (1) ///-references info.referencedFiles.forEach(ref => { const resolvedPath = path.resolve(path.dirname(filename), ref.fileName); @@ -545,6 +581,10 @@ class LanguageServiceHost { }); // (2) import-require statements info.importedFiles.forEach(ref => { + if (!ref.fileName.startsWith('.') || path.extname(ref.fileName) === '') { + // node module? + return; + } const stopDirname = normalize(this.getCurrentDirectory()); let dirname = filename; let found = false; @@ -563,6 +603,10 @@ class LanguageServiceHost { this._dependencies.inertEdge(filename, normalizedPath + '.d.ts'); found = true; } + else if (this.getScriptSnapshot(normalizedPath + '.js')) { + this._dependencies.inertEdge(filename, normalizedPath + '.js'); + found = true; + } } if (!found) { for (const key in this._fileNameToDeclaredModule) { diff --git a/build/lib/tsb/builder.ts b/build/lib/tsb/builder.ts index 70c71591a6e..509284d0cdc 100644 --- a/build/lib/tsb/builder.ts +++ b/build/lib/tsb/builder.ts @@ -42,6 +42,10 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str const _log = config.logFn; const host = new LanguageServiceHost(cmd, projectFile, _log); + + const outHost = new LanguageServiceHost({ ...cmd, options: { ...cmd.options, sourceRoot: cmd.options.outDir } }, cmd.options.outDir ?? '', _log); + let lastCycleCheckVersion: string; + const service = ts.createLanguageService(host, ts.createDocumentRegistry()); const lastBuildVersion: { [path: string]: string } = Object.create(null); const lastDtsHash: { [path: string]: string } = Object.create(null); @@ -305,6 +309,13 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str lastDtsHash[fileName] = value.signature; filesWithChangedSignature.push(fileName); } + + // line up for cycle check + const jsValue = value.files.find(candidate => candidate.basename.endsWith('.js')); + if (jsValue) { + outHost.addScriptSnapshot(jsValue.path, new ScriptSnapshot(String(jsValue.contents), new Date())); + } + }).catch(e => { // can't just skip this or make a result up.. host.error(`ERROR emitting ${fileName}`); @@ -389,6 +400,7 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str } } + // (last) done else { resolve(); @@ -410,16 +422,40 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str workOnNext(); }).then(() => { + // check for cyclic dependencies + const thisCycleCheckVersion = outHost.getProjectVersion(); + if (thisCycleCheckVersion === lastCycleCheckVersion) { + return; + } + const oneCycle = outHost.hasCyclicDependency(); + lastCycleCheckVersion = thisCycleCheckVersion; + delete oldErrors[projectFile]; + + if (oneCycle) { + const cycleError: ts.Diagnostic = { + category: ts.DiagnosticCategory.Error, + code: 1, + file: undefined, + start: undefined, + length: undefined, + messageText: `CYCLIC dependency between ${oneCycle}` + }; + onError(cycleError); + newErrors[projectFile] = [cycleError]; + } + + }).then(() => { + // store the build versions to not rebuilt the next time newLastBuildVersion.forEach((value, key) => { lastBuildVersion[key] = value; }); // print old errors and keep them - utils.collections.forEach(oldErrors, entry => { - entry.value.forEach(diag => onError(diag)); - newErrors[entry.key] = entry.value; - }); + for (const [key, value] of Object.entries(oldErrors)) { + value.forEach(diag => onError(diag)); + newErrors[key] = value; + } oldErrors = newErrors; // print stats @@ -503,7 +539,7 @@ class LanguageServiceHost implements ts.LanguageServiceHost { this._snapshots = Object.create(null); this._filesInProject = new Set(_cmdLine.fileNames); this._filesAdded = new Set(); - this._dependencies = new utils.graph.Graph(s => s); + this._dependencies = new utils.graph.Graph(); this._dependenciesRecomputeList = []; this._fileNameToDeclaredModule = Object.create(null); @@ -576,10 +612,6 @@ class LanguageServiceHost implements ts.LanguageServiceHost { } if (!old || old.getVersion() !== snapshot.getVersion()) { this._dependenciesRecomputeList.push(filename); - const node = this._dependencies.lookup(filename); - if (node) { - node.outgoing = Object.create(null); - } // (cheap) check for declare module LanguageServiceHost._declareModule.lastIndex = 0; @@ -628,10 +660,21 @@ class LanguageServiceHost implements ts.LanguageServiceHost { filename = normalize(filename); const node = this._dependencies.lookup(filename); if (node) { - utils.collections.forEach(node.incoming, entry => target.push(entry.key)); + node.incoming.forEach(entry => target.push(entry.data)); } } + hasCyclicDependency(): string | undefined { + // Ensure dependencies are up to date + while (this._dependenciesRecomputeList.length) { + this._processFile(this._dependenciesRecomputeList.pop()!); + } + const cycle = this._dependencies.findCycle(); + return cycle + ? cycle.join(' -> ') + : undefined; + } + _processFile(filename: string): void { if (filename.match(/.*\.d\.ts$/)) { return; @@ -644,6 +687,9 @@ class LanguageServiceHost implements ts.LanguageServiceHost { } const info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true); + // (0) clear out old dependencies + this._dependencies.resetNode(filename); + // (1) ///-references info.referencedFiles.forEach(ref => { const resolvedPath = path.resolve(path.dirname(filename), ref.fileName); @@ -654,10 +700,18 @@ class LanguageServiceHost implements ts.LanguageServiceHost { // (2) import-require statements info.importedFiles.forEach(ref => { + + if (!ref.fileName.startsWith('.') || path.extname(ref.fileName) === '') { + // node module? + return; + } + + const stopDirname = normalize(this.getCurrentDirectory()); let dirname = filename; let found = false; + while (!found && dirname.indexOf(stopDirname) === 0) { dirname = path.dirname(dirname); let resolvedPath = path.resolve(dirname, ref.fileName); @@ -673,6 +727,10 @@ class LanguageServiceHost implements ts.LanguageServiceHost { } else if (this.getScriptSnapshot(normalizedPath + '.d.ts')) { this._dependencies.inertEdge(filename, normalizedPath + '.d.ts'); found = true; + + } else if (this.getScriptSnapshot(normalizedPath + '.js')) { + this._dependencies.inertEdge(filename, normalizedPath + '.js'); + found = true; } } diff --git a/build/lib/tsb/utils.js b/build/lib/tsb/utils.js index 6ea66221b1b..29cccf0f833 100644 --- a/build/lib/tsb/utils.js +++ b/build/lib/tsb/utils.js @@ -4,54 +4,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.graph = exports.strings = exports.collections = void 0; -var collections; -(function (collections) { - const hasOwnProperty = Object.prototype.hasOwnProperty; - function lookup(collection, key) { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } - return null; - } - collections.lookup = lookup; - function insert(collection, key, value) { - collection[key] = value; - } - collections.insert = insert; - function lookupOrInsert(collection, key, value) { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } - else { - collection[key] = value; - return value; - } - } - collections.lookupOrInsert = lookupOrInsert; - function forEach(collection, callback) { - for (const key in collection) { - if (hasOwnProperty.call(collection, key)) { - callback({ - key: key, - value: collection[key] - }); - } - } - } - collections.forEach = forEach; - function contains(collection, key) { - return hasOwnProperty.call(collection, key); - } - collections.contains = contains; -})(collections || (exports.collections = collections = {})); +exports.graph = exports.strings = void 0; var strings; (function (strings) { - /** - * The empty string. The one and only. - */ - strings.empty = ''; - strings.eolUnix = '\r\n'; function format(value, ...rest) { return value.replace(/({\d+})/g, function (match) { const index = Number(match.substring(1, match.length - 1)); @@ -62,63 +17,83 @@ var strings; })(strings || (exports.strings = strings = {})); var graph; (function (graph) { - function newNode(data) { - return { - data: data, - incoming: {}, - outgoing: {} - }; + class Node { + data; + incoming = new Map(); + outgoing = new Map(); + constructor(data) { + this.data = data; + } } - graph.newNode = newNode; + graph.Node = Node; class Graph { - _hashFn; - _nodes = {}; - constructor(_hashFn) { - this._hashFn = _hashFn; - // empty - } - traverse(start, inwards, callback) { - const startNode = this.lookup(start); - if (!startNode) { - return; - } - this._traverse(startNode, inwards, {}, callback); - } - _traverse(node, inwards, seen, callback) { - const key = this._hashFn(node.data); - if (collections.contains(seen, key)) { - return; - } - seen[key] = true; - callback(node.data); - const nodes = inwards ? node.outgoing : node.incoming; - collections.forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback)); - } + _nodes = new Map(); inertEdge(from, to) { const fromNode = this.lookupOrInsertNode(from); const toNode = this.lookupOrInsertNode(to); - fromNode.outgoing[this._hashFn(to)] = toNode; - toNode.incoming[this._hashFn(from)] = fromNode; + fromNode.outgoing.set(toNode.data, toNode); + toNode.incoming.set(fromNode.data, fromNode); } - removeNode(data) { - const key = this._hashFn(data); - delete this._nodes[key]; - collections.forEach(this._nodes, (entry) => { - delete entry.value.outgoing[key]; - delete entry.value.incoming[key]; - }); + resetNode(data) { + const node = this._nodes.get(data); + if (!node) { + return; + } + for (const outDep of node.outgoing.values()) { + outDep.incoming.delete(node.data); + } + node.outgoing.clear(); } lookupOrInsertNode(data) { - const key = this._hashFn(data); - let node = collections.lookup(this._nodes, key); + let node = this._nodes.get(data); if (!node) { - node = newNode(data); - this._nodes[key] = node; + node = new Node(data); + this._nodes.set(data, node); } return node; } lookup(data) { - return collections.lookup(this._nodes, this._hashFn(data)); + return this._nodes.get(data) ?? null; + } + findCycle() { + let result; + let foundStartNodes = false; + const checked = new Set(); + for (const [_start, value] of this._nodes) { + if (Object.values(value.incoming).length > 0) { + continue; + } + foundStartNodes = true; + const dfs = (node, visited) => { + if (checked.has(node)) { + return; + } + if (visited.has(node)) { + result = [...visited, node].map(n => n.data); + const idx = result.indexOf(node.data); + result = result.slice(idx); + return; + } + visited.add(node); + for (const child of Object.values(node.outgoing)) { + dfs(child, visited); + if (result) { + break; + } + } + visited.delete(node); + checked.add(node); + }; + dfs(value, new Set()); + if (result) { + break; + } + } + if (!foundStartNodes) { + // everything is a cycle + return Array.from(this._nodes.keys()); + } + return result; } } graph.Graph = Graph; diff --git a/build/lib/tsb/utils.ts b/build/lib/tsb/utils.ts index 3b003e3a6e1..59d1cab36d3 100644 --- a/build/lib/tsb/utils.ts +++ b/build/lib/tsb/utils.ts @@ -3,54 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export module collections { - - const hasOwnProperty = Object.prototype.hasOwnProperty; - - export function lookup(collection: { [keys: string]: T }, key: string): T | null { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } - return null; - } - - export function insert(collection: { [keys: string]: T }, key: string, value: T): void { - collection[key] = value; - } - - export function lookupOrInsert(collection: { [keys: string]: T }, key: string, value: T): T { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } else { - collection[key] = value; - return value; - } - } - - export function forEach(collection: { [keys: string]: T }, callback: (entry: { key: string; value: T }) => void): void { - for (const key in collection) { - if (hasOwnProperty.call(collection, key)) { - callback({ - key: key, - value: collection[key] - }); - } - } - } - - export function contains(collection: { [keys: string]: any }, key: string): boolean { - return hasOwnProperty.call(collection, key); - } -} - -export module strings { - - /** - * The empty string. The one and only. - */ - export const empty = ''; - - export const eolUnix = '\r\n'; +export namespace strings { export function format(value: string, ...rest: any[]): string { return value.replace(/({\d+})/g, function (match) { @@ -60,80 +13,104 @@ export module strings { } } -export module graph { +export namespace graph { - export interface Node { - data: T; - incoming: { [key: string]: Node }; - outgoing: { [key: string]: Node }; - } + export class Node { - export function newNode(data: T): Node { - return { - data: data, - incoming: {}, - outgoing: {} - }; - } + readonly incoming = new Map>(); + readonly outgoing = new Map>(); - export class Graph { - - private _nodes: { [key: string]: Node } = {}; + constructor(readonly data: T) { - constructor(private _hashFn: (element: T) => string) { - // empty } + } - traverse(start: T, inwards: boolean, callback: (data: T) => void): void { - const startNode = this.lookup(start); - if (!startNode) { - return; - } - this._traverse(startNode, inwards, {}, callback); - } + export class Graph { - private _traverse(node: Node, inwards: boolean, seen: { [key: string]: boolean }, callback: (data: T) => void): void { - const key = this._hashFn(node.data); - if (collections.contains(seen, key)) { - return; - } - seen[key] = true; - callback(node.data); - const nodes = inwards ? node.outgoing : node.incoming; - collections.forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback)); - } + private _nodes = new Map>(); inertEdge(from: T, to: T): void { const fromNode = this.lookupOrInsertNode(from); const toNode = this.lookupOrInsertNode(to); - fromNode.outgoing[this._hashFn(to)] = toNode; - toNode.incoming[this._hashFn(from)] = fromNode; + fromNode.outgoing.set(toNode.data, toNode); + toNode.incoming.set(fromNode.data, fromNode); } - removeNode(data: T): void { - const key = this._hashFn(data); - delete this._nodes[key]; - collections.forEach(this._nodes, (entry) => { - delete entry.value.outgoing[key]; - delete entry.value.incoming[key]; - }); + resetNode(data: T): void { + const node = this._nodes.get(data); + if (!node) { + return; + } + for (const outDep of node.outgoing.values()) { + outDep.incoming.delete(node.data); + } + node.outgoing.clear(); } lookupOrInsertNode(data: T): Node { - const key = this._hashFn(data); - let node = collections.lookup(this._nodes, key); + let node = this._nodes.get(data); if (!node) { - node = newNode(data); - this._nodes[key] = node; + node = new Node(data); + this._nodes.set(data, node); } return node; } lookup(data: T): Node | null { - return collections.lookup(this._nodes, this._hashFn(data)); + return this._nodes.get(data) ?? null; + } + + findCycle(): T[] | undefined { + + let result: T[] | undefined; + let foundStartNodes = false; + const checked = new Set>(); + + for (const [_start, value] of this._nodes) { + + if (Object.values(value.incoming).length > 0) { + continue; + } + + foundStartNodes = true; + + const dfs = (node: Node, visited: Set>) => { + + if (checked.has(node)) { + return; + } + + if (visited.has(node)) { + result = [...visited, node].map(n => n.data); + const idx = result.indexOf(node.data); + result = result.slice(idx); + return; + } + visited.add(node); + for (const child of Object.values(node.outgoing)) { + dfs(child, visited); + if (result) { + break; + } + } + visited.delete(node); + checked.add(node); + }; + dfs(value, new Set()); + if (result) { + break; + } + } + + if (!foundStartNodes) { + // everything is a cycle + return Array.from(this._nodes.keys()); + } + + return result; } } diff --git a/src/vs/editor/common/core/textEdit.ts b/src/vs/editor/common/core/textEdit.ts index 9307784837f..83baa3bff43 100644 --- a/src/vs/editor/common/core/textEdit.ts +++ b/src/vs/editor/common/core/textEdit.ts @@ -8,7 +8,6 @@ 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 { LineEdit } from './lineEdit.js'; import { LineRange } from './lineRange.js'; import { OffsetEdit } from './offsetEdit.js'; import { Position } from './position.js'; @@ -399,16 +398,3 @@ export class StringText extends AbstractText { return this._t.textLength; } } - -export class BasedTextEdit { - constructor( - public readonly base: AbstractText, - public readonly edit: TextEdit, - ) { - } - - toString() { - const lineEdit = LineEdit.fromTextEdit(this.edit, this.base); - return lineEdit.humanReadablePatch(this.base.getLines()); - } -} diff --git a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPicker.ts b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPicker.ts new file mode 100644 index 00000000000..031e481cf24 --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPicker.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IPartialEditorMouseEvent, MouseTargetType } from '../../../../browser/editorBrowser.js'; +import { ColorDecorationInjectedTextMarker } from '../colorDetector.js'; + + +export function isOnColorDecorator(mouseEvent: IPartialEditorMouseEvent): boolean { + const target = mouseEvent.target; + return !!target + && target.type === MouseTargetType.CONTENT_TEXT + && target.detail.injectedText?.options.attachedData === ColorDecorationInjectedTextMarker; +} diff --git a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts index 63ed7dc7259..bef71cbf912 100644 --- a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts +++ b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent, MouseTargetType } from '../../../../browser/editorBrowser.js'; +import { ICodeEditor, IEditorMouseEvent } from '../../../../browser/editorBrowser.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { Range } from '../../../../common/core/range.js'; import { IEditorContribution } from '../../../../common/editorCommon.js'; -import { ColorDecorationInjectedTextMarker } from '../colorDetector.js'; import { ContentHoverController } from '../../../hover/browser/contentHoverController.js'; import { HoverStartMode, HoverStartSource } from '../../../hover/browser/hoverOperation.js'; +import { isOnColorDecorator } from './hoverColorPicker.js'; export class HoverColorPickerContribution extends Disposable implements IEditorContribution { @@ -52,10 +52,3 @@ export class HoverColorPickerContribution extends Disposable implements IEditorC hoverController.showContentHover(range, HoverStartMode.Immediate, HoverStartSource.Click, false); } } - -export function isOnColorDecorator(mouseEvent: IPartialEditorMouseEvent): boolean { - const target = mouseEvent.target; - return !!target - && target.type === MouseTargetType.CONTENT_TEXT - && target.detail.injectedText?.options.attachedData === ColorDecorationInjectedTextMarker; -} diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index 9b8252eec14..26a7115f3ad 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -22,7 +22,7 @@ import { isMousePositionWithinElement } from './hoverUtils.js'; import { ContentHoverWidgetWrapper } from './contentHoverWidgetWrapper.js'; import './hover.css'; import { Emitter } from '../../../../base/common/event.js'; -import { isOnColorDecorator } from '../../colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.js'; +import { isOnColorDecorator } from '../../colorPicker/browser/hoverColorPicker/hoverColorPicker.js'; // sticky hover widget which doesn't disappear on focus out and such const _sticky = false diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 8880e5c4d59..248bbd7da53 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { AsyncIterableObject } from '../../../../../base/common/async.js'; +import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; @@ -20,6 +22,7 @@ import { TerminalLocation } from '../../../../../platform/terminal/common/termin import { IUntitledTextResourceEditorInput } from '../../../../common/editor.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { accessibleViewInCodeBlock } from '../../../accessibility/browser/accessibilityConfiguration.js'; +import { InlineChatController } from '../../../inlineChat/browser/inlineChatController.js'; import { ITerminalEditorService, ITerminalGroupService, ITerminalService } from '../../../terminal/browser/terminal.js'; import { ChatAgentLocation } from '../../common/chatAgents.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; @@ -529,10 +532,38 @@ export function registerChatCodeCompareBlockActions() { async runWithContext(accessor: ServicesAccessor, context: ICodeCompareBlockActionContext): Promise { - const instaService = accessor.get(IInstantiationService); + const editorService = accessor.get(ICodeEditorService); - const editor = instaService.createInstance(DefaultChatTextEditor); - await editor.preview(context.element, context.edit); + const item = context.edit; + const response = context.element; + + if (item.state?.applied) { + // already applied + return false; + } + + if (!response.response.value.includes(item)) { + // bogous item + return false; + } + + const firstEdit = item.edits[0]?.[0]; + if (!firstEdit) { + return false; + } + const textEdits = AsyncIterableObject.fromArray(item.edits); + + const editorToApply = await editorService.openCodeEditor({ resource: item.uri }, null); + if (editorToApply) { + const inlineChatController = InlineChatController.get(editorToApply); + if (inlineChatController) { + editorToApply.revealLineInCenterIfOutsideViewport(firstEdit.range.startLineNumber); + inlineChatController.reviewEdits(firstEdit.range, textEdits, CancellationToken.None); + response.setEditApplied(item, 1); + return true; + } + } + return false; } }); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts index 912b37c6624..af6ffc0640e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts @@ -487,14 +487,6 @@ class ChatDecorationsProvider extends Disposable implements IDecorationsProvider } export class ChatEditingMultiDiffSourceResolver implements IMultiDiffSourceResolver { - public static readonly scheme = CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME; - - public static getMultiDiffSourceUri(): URI { - return URI.from({ - scheme: ChatEditingMultiDiffSourceResolver.scheme, - path: '', - }); - } constructor( private readonly _currentSession: IObservable, @@ -502,7 +494,7 @@ export class ChatEditingMultiDiffSourceResolver implements IMultiDiffSourceResol ) { } canHandleUri(uri: URI): boolean { - return uri.scheme === ChatEditingMultiDiffSourceResolver.scheme; + return uri.scheme === CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME; } async resolveDiffSource(uri: URI): Promise { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index 7b770ec4e97..6f24595cbad 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts @@ -30,9 +30,8 @@ import { IEditorService } from '../../../../services/editor/common/editorService import { MultiDiffEditor } from '../../../multiDiffEditor/browser/multiDiffEditor.js'; import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiffEditorInput.js'; import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; -import { ChatEditingSessionChangeType, ChatEditingSessionState, ChatEditKind, IChatEditingSession, IModifiedFileEntry, WorkingSetDisplayMetadata, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { ChatEditingSessionChangeType, ChatEditingSessionState, ChatEditKind, getMultiDiffSourceUri, IChatEditingSession, IModifiedFileEntry, WorkingSetDisplayMetadata, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { IChatResponseModel } from '../../common/chatModel.js'; -import { ChatEditingMultiDiffSourceResolver } from './chatEditingService.js'; import { ChatEditingModifiedFileEntry, IModifiedEntryTelemetryInfo, ISnapshotEntry } from './chatEditingModifiedFileEntry.js'; import { ChatEditingTextModelContentProvider } from './chatEditingTextModelContentProviders.js'; import { Schemas } from '../../../../../base/common/network.js'; @@ -484,7 +483,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } } const input = MultiDiffEditorInput.fromResourceMultiDiffEditorInput({ - multiDiffSource: ChatEditingMultiDiffSourceResolver.getMultiDiffSourceUri(), + multiDiffSource: getMultiDiffSourceUri(), label: localize('multiDiffEditorInput.name', "Suggested Edits") }, this._instantiationService); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts index 0646a4ed503..b7f3ae1d0b4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorActions.ts @@ -19,7 +19,7 @@ import { ChatContextKeys } from '../common/chatContextKeys.js'; import { isEqual } from '../../../../base/common/resources.js'; import { Range } from '../../../../editor/common/core/range.js'; import { getNotebookEditorFromEditorPane } from '../../notebook/browser/notebookBrowser.js'; -import { ctxNotebookHasEditorModification } from '../../notebook/browser/contrib/chatEdit/notebookChatEditController.js'; +import { ctxNotebookHasEditorModification } from '../../notebook/browser/contrib/chatEdit/notebookChatEditContext.js'; abstract class NavigateAction extends Action2 { diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 14e5cf66a58..ee062a4c359 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -52,7 +52,7 @@ import { IChatAgentService } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { CHAT_CATEGORY } from './actions/chatActions.js'; import { ChatViewId, EditsViewId, ensureSideBarChatViewSize, IChatWidget, showChatView, showEditsView } from './chat.js'; -import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID } from './chatViewPane.js'; +import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID, SetupWelcomeViewCondition } from './chatViewPane.js'; import { ChatViewsWelcomeExtensions, IChatViewsWelcomeContributionRegistry } from './viewsWelcome/chatViewsWelcome.js'; import { IChatQuotasService } from './chatQuotasService.js'; import { mainWindow } from '../../../../base/browser/window.js'; @@ -95,25 +95,6 @@ enum ChatEntitlement { const TRIGGER_SETUP_COMMAND_ID = 'workbench.action.chat.triggerSetup'; const TRIGGER_SETUP_COMMAND_LABEL = localize2('triggerChatSetup', "Use AI Features with Copilot for Free..."); -export const SetupWelcomeViewKeys = new Set([ChatContextKeys.Setup.triggered.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Setup.signedOut.key, ChatContextKeys.Setup.canSignUp.key]); -export const SetupWelcomeViewCondition = ContextKeyExpr.and( - ContextKeyExpr.has('config.chat.experimental.offerSetup'), - ContextKeyExpr.or( - ContextKeyExpr.and( - ChatContextKeys.Setup.triggered, - ChatContextKeys.Setup.installed.negate() - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.canSignUp, - ChatContextKeys.Setup.installed - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.signedOut, - ChatContextKeys.Setup.installed - ) - ) -)!; - export class ChatSetupContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.chat.setup'; diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index 485adf05ab9..8e0e8bd85ff 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -8,7 +8,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -27,10 +27,10 @@ import { SIDE_BAR_FOREGROUND } from '../../../common/theme.js'; import { IViewDescriptorService } from '../../../common/views.js'; import { IChatViewTitleActionContext } from '../common/chatActions.js'; import { ChatAgentLocation, IChatAgentService } from '../common/chatAgents.js'; +import { ChatContextKeys } from '../common/chatContextKeys.js'; import { ChatModelInitState, IChatModel } from '../common/chatModel.js'; import { CHAT_PROVIDER_ID } from '../common/chatParticipantContribTypes.js'; import { IChatService } from '../common/chatService.js'; -import { SetupWelcomeViewCondition, SetupWelcomeViewKeys } from './chatSetup.js'; import { ChatWidget, IChatViewState } from './chatWidget.js'; import { ChatViewWelcomeController, IViewWelcomeDelegate } from './viewsWelcome/chatViewWelcomeController.js'; @@ -279,3 +279,22 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { } } } + +export const SetupWelcomeViewKeys = new Set([ChatContextKeys.Setup.triggered.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Setup.signedOut.key, ChatContextKeys.Setup.canSignUp.key]); +export const SetupWelcomeViewCondition = ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.experimental.offerSetup'), + ContextKeyExpr.or( + ContextKeyExpr.and( + ChatContextKeys.Setup.triggered, + ChatContextKeys.Setup.installed.negate() + ), + ContextKeyExpr.and( + ChatContextKeys.Setup.canSignUp, + ChatContextKeys.Setup.installed + ), + ContextKeyExpr.and( + ChatContextKeys.Setup.signedOut, + ChatContextKeys.Setup.installed + ) + ) +)!; diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index 1c05029923c..6f34a50203b 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -8,7 +8,7 @@ import './codeBlockPart.css'; import * as dom from '../../../../base/browser/dom.js'; import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; -import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { combinedDisposable, Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; @@ -69,8 +69,6 @@ import { ChatTreeItem } from './chat.js'; import { IChatRendererDelegate } from './chatListRenderer.js'; import { ChatEditorOptions } from './chatOptions.js'; import { emptyProgressRunner, IEditorProgressService } from '../../../../platform/progress/common/progress.js'; -import { AsyncIterableObject } from '../../../../base/common/async.js'; -import { InlineChatController } from '../../inlineChat/browser/inlineChatController.js'; const $ = dom.$; @@ -941,37 +939,5 @@ export class DefaultChatTextEditor { response.setEditApplied(item, -1); } - async preview(response: IChatResponseModel | IChatResponseViewModel, item: IChatTextEditGroup) { - if (item.state?.applied) { - // already applied - return false; - } - if (!response.response.value.includes(item)) { - // bogous item - return false; - } - - const firstEdit = item.edits[0]?.[0]; - if (!firstEdit) { - return false; - } - const textEdits = AsyncIterableObject.fromArray(item.edits); - - const editorToApply = await this.editorService.openCodeEditor({ resource: item.uri }, null); - if (editorToApply) { - const inlineChatController = InlineChatController.get(editorToApply); - if (inlineChatController) { - const tokenSource = new CancellationTokenSource(); - editorToApply.revealLineInCenterIfOutsideViewport(firstEdit.range.startLineNumber); - const promise = inlineChatController.reviewEdits(firstEdit.range, textEdits, tokenSource.token); - response.setEditApplied(item, 1); - promise.finally(() => { - tokenSource.dispose(); - }); - return true; - } - } - return false; - } } diff --git a/src/vs/workbench/contrib/chat/common/chatEditingService.ts b/src/vs/workbench/contrib/chat/common/chatEditingService.ts index c0598e39eba..ed970c30625 100644 --- a/src/vs/workbench/contrib/chat/common/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/common/chatEditingService.ts @@ -163,3 +163,10 @@ export interface IChatEditingActionContext { export function isChatEditingActionContext(thing: unknown): thing is IChatEditingActionContext { return typeof thing === 'object' && !!thing && 'sessionId' in thing; } + +export function getMultiDiffSourceUri(): URI { + return URI.from({ + scheme: CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, + path: '', + }); +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts new file mode 100644 index 00000000000..ff706d889be --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { RawContextKey } from '../../../../../../platform/contextkey/common/contextkey.js'; + +export const ctxNotebookHasEditorModification = new RawContextKey('chat.hasNotebookEditorModifications', undefined, localize('chat.hasNotebookEditorModifications', "The current Notebook editor contains chat modifications")); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts index 3185b503d56..766e43a951c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts @@ -17,14 +17,12 @@ import { INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceF import { debouncedObservable2 } from '../../../../../../base/common/observableInternal/utils.js'; import { CellDiffInfo } from '../../diff/notebookDiffViewModel.js'; import { NotebookChatActionsOverlayController } from './notebookChatActionsOverlay.js'; -import { IContextKey, IContextKeyService, RawContextKey } from '../../../../../../platform/contextkey/common/contextkey.js'; -import { localize } from '../../../../../../nls.js'; +import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; import { InstantiationType, registerSingleton } from '../../../../../../platform/instantiation/common/extensions.js'; import { INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory } from './notebookOriginalCellModelFactory.js'; import { Event } from '../../../../../../base/common/event.js'; - -export const ctxNotebookHasEditorModification = new RawContextKey('chat.hasNotebookEditorModifications', undefined, localize('chat.hasNotebookEditorModifications', "The current Notebook editor contains chat modifications")); +import { ctxNotebookHasEditorModification } from './notebookChatEditContext.js'; export class NotebookChatEditorControllerContrib extends Disposable implements INotebookEditorContribution { From d18a40011bb2425429a3abf8b719cb8d5bcc558c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 Dec 2024 11:34:16 +0100 Subject: [PATCH 096/479] fix #235763 (#235810) --- .../common/abstractExtensionManagementService.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index d78d2ef9c73..59f359d659c 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -76,10 +76,9 @@ export abstract class CommontExtensionManagementService extends Disposable imple } if (!(await this.isExtensionPlatformCompatible(extension))) { - const productName = isWeb ? nls.localize('VS Code for Web', "{0} for the Web", this.productService.nameLong) : this.productService.nameLong; const learnLink = isWeb ? 'https://aka.ms/vscode-web-extensions-guide' : 'https://aka.ms/vscode-platform-specific-extensions'; - return new MarkdownString(`${nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", - extension.displayName ?? extension.identifier.id, productName, TargetPlatformToString(await this.getTargetPlatform()))} [${nls.localize('learn why', "Learn Why")}](${learnLink})`); + return new MarkdownString(`${nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for the {2}.", + extension.displayName ?? extension.identifier.id, this.productService.nameLong, TargetPlatformToString(await this.getTargetPlatform()))} [${nls.localize('learn why', "Learn Why")}](${learnLink})`); } return true; @@ -657,7 +656,7 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio else { if (await this.canInstall(extension) !== true) { const targetPlatform = await this.getTargetPlatform(); - throw new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.IncompatibleTargetPlatform); + throw new ExtensionManagementError(nls.localize('incompatible platform', "The '{0}' extension is not available in {1} for the {2}.", extension.identifier.id, this.productService.nameLong, TargetPlatformToString(targetPlatform)), ExtensionManagementErrorCode.IncompatibleTargetPlatform); } compatibleExtension = await this.getCompatibleVersion(extension, sameVersion, installPreRelease, productVersion); From efb1463cb5013b44b275b601936d9dfb3054c7da Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 11:39:14 +0100 Subject: [PATCH 097/479] Finalize file level comments Fixes #214327 --- .../common/extensionsApiProposals.ts | 3 -- .../workbench/api/common/extHostComments.ts | 5 +-- src/vscode-dts/vscode.d.ts | 22 ++++++++-- .../vscode.proposed.fileComments.d.ts | 42 ------------------- 4 files changed, 20 insertions(+), 52 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.fileComments.d.ts diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 1aec67707c7..9a6636005b3 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -189,9 +189,6 @@ const _allApiProposals = { externalUriOpener: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts', }, - fileComments: { - proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fileComments.d.ts', - }, fileSearchProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts', }, diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index 74eca08c943..908d094ec00 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -211,7 +211,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo const document = await documents.ensureDocumentData(URI.revive(uriComponents)); return asPromise(async () => { - const rangesResult = await (commentController.commentingRangeProvider as vscode.CommentingRangeProvider2).provideCommentingRanges(document.document, token); + const rangesResult = await commentController.commentingRangeProvider?.provideCommentingRanges(document.document, token); let ranges: { ranges: vscode.Range[]; fileComments: boolean } | undefined; if (Array.isArray(rangesResult)) { ranges = { @@ -695,9 +695,6 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo } createCommentThread(resource: vscode.Uri, range: vscode.Range | undefined, comments: vscode.Comment[]): ExtHostCommentThread { - if (range === undefined) { - checkProposedApiEnabled(this._extension, 'fileComments'); - } const commentThread = new ExtHostCommentThread(this.id, this.handle, undefined, resource, range, comments, this._extension, false); this._threads.set(commentThread.handle, commentThread); return commentThread; diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 4fd34315755..c9ecff2b0b9 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -16810,9 +16810,10 @@ declare module 'vscode' { /** * The range the comment thread is located within the document. The thread icon will be shown - * at the last line of the range. + * at the last line of the range. When set to undefined, the comment will be associated with the + * file, and not a specific range. */ - range: Range; + range: Range | undefined; /** * The ordered comments of the thread. @@ -16981,6 +16982,21 @@ declare module 'vscode' { text: string; } + /** + * The ranges a CommentingRangeProvider enables commenting on. + */ + export interface CommentingRanges { + /** + * Enables comments to be added to a file without a specific range. + */ + enableFileComments: boolean; + + /** + * The ranges which allow new comment threads creation. + */ + ranges?: Range[]; + } + /** * Commenting range provider for a {@link CommentController comment controller}. */ @@ -16988,7 +17004,7 @@ declare module 'vscode' { /** * Provide a list of ranges which allow new comment threads creation or null for a given document */ - provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; + provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; } /** diff --git a/src/vscode-dts/vscode.proposed.fileComments.d.ts b/src/vscode-dts/vscode.proposed.fileComments.d.ts deleted file mode 100644 index 96e9b181bc6..00000000000 --- a/src/vscode-dts/vscode.proposed.fileComments.d.ts +++ /dev/null @@ -1,42 +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' { - - export interface CommentThread2 { - /** - * The range the comment thread is located within the document. The thread icon will be shown - * at the last line of the range. When set to undefined, the comment will be associated with the - * file, and not a specific range. - */ - range: Range | undefined; - } - - /** - * The ranges a CommentingRangeProvider enables commenting on. - */ - export interface CommentingRanges { - /** - * Enables comments to be added to a file without a specific range. - */ - enableFileComments: boolean; - - /** - * The ranges which allow new comment threads creation. - */ - ranges?: Range[]; - } - - export interface CommentController { - createCommentThread(uri: Uri, range: Range | undefined, comments: readonly Comment[]): CommentThread | CommentThread2; - } - - export interface CommentingRangeProvider2 { - /** - * Provide a list of ranges which allow new comment threads creation or null for a given document - */ - provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; - } -} From 4fb4fc90310a408a4f5c6216372e634634ae8ea0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 11:42:59 +0100 Subject: [PATCH 098/479] Comment text cut off in Comments view (#235813) Fixes #226415 --- src/vs/workbench/contrib/comments/browser/media/panel.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/media/panel.css b/src/vs/workbench/contrib/comments/browser/media/panel.css index 8527341bdae..37930e9119f 100644 --- a/src/vs/workbench/contrib/comments/browser/media/panel.css +++ b/src/vs/workbench/contrib/comments/browser/media/panel.css @@ -94,10 +94,6 @@ padding-right: 5px; } -.comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-metadata .text * { - max-width: 700px; -} - .comments-panel .comments-panel-container .tree-container .comment-thread-container .range { opacity: 0.8; } From 06abfd26c8eed3c6ca6e0fafddd736848ae5ce6d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Dec 2024 12:52:05 +0100 Subject: [PATCH 099/479] fixes https://github.com/microsoft/vscode-copilot/issues/7665 (#235818) --- src/vs/workbench/contrib/chat/browser/media/chat.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 502ef69725e..fdcc82938f1 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -1015,6 +1015,7 @@ have to be updated for changes to the rules above, or to support more deeply nes .interactive-session .interactive-input-part.compact .chat-attached-context { padding-top: 8px; + padding-bottom: 0px; display: flex; gap: 4px; flex-wrap: wrap; From a444d2419c3ba82688fe04b422c48cbc0a17bf43 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 11 Dec 2024 12:54:17 +0100 Subject: [PATCH 100/479] :up: distro (#235817) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 12ff0ff2f0b..5237906a2c7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.97.0", - "distro": "c883c91dadf5f063b26c86ffe01851acee3747c6", + "distro": "45ffe59ad7c51acf28b541a2aa76cc73437ff118", "author": { "name": "Microsoft Corporation" }, From b4364975530e0bae1d70a64e9ef87a001b8530b3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 11 Dec 2024 12:56:37 +0100 Subject: [PATCH 101/479] chat - setup tweaks (#235797) --- .../contrib/chat/browser/chatSetup.ts | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index ee062a4c359..d23c468f229 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -36,7 +36,7 @@ import { IProgressService, ProgressLocation } from '../../../../platform/progres import { Registry } from '../../../../platform/registry/common/platform.js'; import { asText, IRequestService } from '../../../../platform/request/common/request.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { ITelemetryService, TelemetryLevel } from '../../../../platform/telemetry/common/telemetry.js'; import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; @@ -67,12 +67,14 @@ const defaultChat = { termsStatementUrl: product.defaultChatAgent?.termsStatementUrl ?? '', privacyStatementUrl: product.defaultChatAgent?.privacyStatementUrl ?? '', skusDocumentationUrl: product.defaultChatAgent?.skusDocumentationUrl ?? '', + publicCodeMatchesUrl: product.defaultChatAgent?.publicCodeMatchesUrl ?? '', upgradePlanUrl: product.defaultChatAgent?.upgradePlanUrl ?? '', providerId: product.defaultChatAgent?.providerId ?? '', providerName: product.defaultChatAgent?.providerName ?? '', providerScopes: product.defaultChatAgent?.providerScopes ?? [[]], entitlementUrl: product.defaultChatAgent?.entitlementUrl ?? '', entitlementSignupLimitedUrl: product.defaultChatAgent?.entitlementSignupLimitedUrl ?? '', + manageSettingsUrl: product.defaultChatAgent?.manageSettingsUrl ?? '', }; enum ChatEntitlement { @@ -562,7 +564,7 @@ class ChatSetupRequests extends Disposable { async signUpLimited(session: AuthenticationSession): Promise { const body = { - restricted_telemetry: 'disabled', + restricted_telemetry: this.telemetryService.telemetryLevel === TelemetryLevel.NONE ? 'disabled' : 'enabled', public_code_suggestions: 'enabled' }; @@ -809,7 +811,8 @@ class ChatSetupWelcomeContent extends Disposable { private readonly context: ChatSetupContext, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @ICommandService private readonly commandService: ICommandService + @ICommandService private readonly commandService: ICommandService, + @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); @@ -847,9 +850,9 @@ class ChatSetupWelcomeContent extends Disposable { } // Limited SKU - const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); - const limitedSkuHeaderContainer = this.element.appendChild($('p')); - limitedSkuHeaderContainer.appendChild(this._register(markdown.render(new MarkdownString(limitedSkuHeader, { isTrusted: true, supportThemeIcons: true }))).element); + const free = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); + const freeContainer = this.element.appendChild($('p')); + freeContainer.appendChild(this._register(markdown.render(new MarkdownString(free, { isTrusted: true, supportThemeIcons: true }))).element); // Setup Button const actions: IAction[] = []; @@ -875,31 +878,37 @@ class ChatSetupWelcomeContent extends Disposable { const terms = localize({ key: 'termsLabel', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element); + // SKU Settings + const settings = localize({ key: 'termsLabelWithTelemetry', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot may show [public code]({0}) suggestions and collect usage data. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); + const settingsContainer = this.element.appendChild($('p')); + settingsContainer.appendChild(this._register(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element); + // Update based on model state - this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button))); + this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(freeContainer, settingsContainer, button))); } - private update(limitedSkuHeaderContainer: HTMLElement, button: Button | ButtonWithDropdown): void { - let showLimitedSkuHeader: boolean; + private update(freeContainer: HTMLElement, settingsContainer: HTMLElement, button: Button | ButtonWithDropdown): void { + const showSettings = this.telemetryService.telemetryLevel !== TelemetryLevel.NONE; + let showFree: boolean; let buttonLabel: string; switch (this.context.state.entitlement) { case ChatEntitlement.Unknown: - showLimitedSkuHeader = true; + showFree = true; buttonLabel = this.context.state.registered ? localize('signUp', "Sign in to Use Copilot") : localize('signUpFree', "Sign in to Use Copilot for Free"); break; case ChatEntitlement.Unresolved: - showLimitedSkuHeader = true; + showFree = true; buttonLabel = this.context.state.registered ? localize('startUp', "Use Copilot") : localize('startUpLimited', "Use Copilot for Free"); break; case ChatEntitlement.Available: case ChatEntitlement.Limited: - showLimitedSkuHeader = true; + showFree = true; buttonLabel = localize('startUpLimited', "Use Copilot for Free"); break; case ChatEntitlement.Pro: case ChatEntitlement.Unavailable: - showLimitedSkuHeader = false; + showFree = false; buttonLabel = localize('startUp', "Use Copilot"); break; } @@ -913,7 +922,8 @@ class ChatSetupWelcomeContent extends Disposable { break; } - setVisibility(showLimitedSkuHeader, limitedSkuHeaderContainer); + setVisibility(showFree, freeContainer); + setVisibility(showSettings, settingsContainer); button.label = buttonLabel; button.enabled = this.controller.step === ChatSetupStep.Initial; From 7b7be41b4c64977c1f3ff666748dbb3736ce4b49 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 13:22:25 +0100 Subject: [PATCH 102/479] Toggle Reaction doesn't always show on first load of comments (#235822) Fixes #235821 --- src/vs/workbench/contrib/comments/browser/commentNode.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index e014be03658..a8598ae91e4 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -339,9 +339,8 @@ export class CommentNode extends Disposable { const actions: IAction[] = []; const hasReactionHandler = this.commentService.hasReactionHandler(this.owner); - - if (hasReactionHandler) { - const toggleReactionAction = this.createReactionPicker(this.comment.commentReactions || []); + const toggleReactionAction = hasReactionHandler ? this.createReactionPicker(this.comment.commentReactions || []) : undefined; + if (toggleReactionAction) { actions.push(toggleReactionAction); } @@ -352,7 +351,9 @@ export class CommentNode extends Disposable { if (!this.toolbar && (primary.length || secondary.length)) { this.createToolbar(); } - + if (toggleReactionAction) { + primary.unshift(toggleReactionAction); + } this.toolbar!.setActions(primary, secondary); })); From fc52d27a89df1975fd48da2e07d7a22d0e972c0c Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 11 Dec 2024 13:55:40 +0100 Subject: [PATCH 103/479] Disposing the color picker disposables on rerender (#235820) disposing the color picker disposables on rerender --- .../hoverColorPickerParticipant.ts | 6 +- .../standaloneColorPickerParticipant.ts | 86 ++++++++++++------- .../standaloneColorPickerWidget.ts | 23 +++-- .../contrib/hover/browser/hoverTypes.ts | 2 +- 4 files changed, 70 insertions(+), 47 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts index 119810b7b6d..c850d374443 100644 --- a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts +++ b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts @@ -112,10 +112,8 @@ export class HoverColorPickerParticipant implements IEditorHoverParticipant { + this.color = color; + })); + this._register(colorPickerModel.onDidChangeColor((color: Color) => { + updateColorPresentations(editorModel, colorPickerModel, color, colorHover.range, colorHover); + })); + let editorUpdatedByColorPicker = false; + this._register(editor.onDidChangeModelContent((e) => { + if (editorUpdatedByColorPicker) { + editorUpdatedByColorPicker = false; + } else { + context.hide(); + editor.focus(); + } + })); + updateColorPresentations(editorModel, colorPickerModel, this.color, colorHover.range, colorHover); + } +} + export class StandaloneColorPickerParticipant { public readonly hoverOrdinal: number = 2; - private _color: Color | null = null; + private _renderedParts: StandaloneColorPickerRenderedParts | undefined; constructor( private readonly _editor: ICodeEditor, @@ -80,40 +119,21 @@ export class StandaloneColorPickerParticipant { } } - public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: StandaloneColorPickerHover[]): { disposables: IDisposable; hoverPart: StandaloneColorPickerHover; colorPicker: ColorPickerWidget } | undefined { + public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: StandaloneColorPickerHover[]): StandaloneColorPickerRenderedParts | undefined { if (hoverParts.length === 0 || !this._editor.hasModel()) { return undefined; } - if (context.setMinimumDimensions) { - const minimumHeight = this._editor.getOption(EditorOption.lineHeight) + 8; - context.setMinimumDimensions(new Dimension(302, minimumHeight)); - } + this._setMinimumDimensions(context); + this._renderedParts = new StandaloneColorPickerRenderedParts(this._editor, context, hoverParts[0], this._themeService); + return this._renderedParts; + } - const disposables = new DisposableStore(); - const colorHover = hoverParts[0]; - const editorModel = this._editor.getModel(); - const model = colorHover.model; - const colorPicker = disposables.add(new ColorPickerWidget(context.fragment, model, this._editor.getOption(EditorOption.pixelRatio), this._themeService, ColorPickerWidgetType.Standalone)); + private _setMinimumDimensions(context: IEditorHoverRenderContext): void { + const minimumHeight = this._editor.getOption(EditorOption.lineHeight) + 8; + context.setMinimumDimensions(new Dimension(302, minimumHeight)); + } - let editorUpdatedByColorPicker = false; - const range = new Range(colorHover.range.startLineNumber, colorHover.range.startColumn, colorHover.range.endLineNumber, colorHover.range.endColumn); - const color = colorHover.model.color; - this._color = color; - updateColorPresentations(editorModel, model, color, range, colorHover); - disposables.add(model.onColorFlushed((color: Color) => { - this._color = color; - })); - disposables.add(model.onDidChangeColor((color: Color) => { - updateColorPresentations(editorModel, model, color, range, colorHover); - })); - disposables.add(this._editor.onDidChangeModelContent((e) => { - if (editorUpdatedByColorPicker) { - editorUpdatedByColorPicker = false; - } else { - context.hide(); - this._editor.focus(); - } - })); - return { hoverPart: colorHover, colorPicker, disposables }; + private get _color(): Color | undefined { + return this._renderedParts?.color; } } diff --git a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts index 8fcb698b940..c26966a842f 100644 --- a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import '../colorPicker.css'; -import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { IEditorHoverRenderContext } from '../../../hover/browser/hoverTypes.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../browser/editorBrowser.js'; import { PositionAffinity } from '../../../../common/model.js'; @@ -20,7 +20,7 @@ import { IContextKey } from '../../../../../platform/contextkey/common/contextke import { IRange } from '../../../../common/core/range.js'; import { DefaultDocumentColorProvider } from '../defaultDocumentColorProvider.js'; import { IEditorWorkerService } from '../../../../common/services/editorWorker.js'; -import { StandaloneColorPickerHover, StandaloneColorPickerParticipant } from './standaloneColorPickerParticipant.js'; +import { StandaloneColorPickerHover, StandaloneColorPickerParticipant, StandaloneColorPickerRenderedParts } from './standaloneColorPickerParticipant.js'; import * as dom from '../../../../../base/browser/dom.js'; import { InsertButton } from '../colorPickerParts/colorPickerInsertButton.js'; import { IHoverService } from '../../../../../platform/hover/browser/hover.js'; @@ -51,6 +51,9 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW private readonly _onResult = this._register(new Emitter()); public readonly onResult = this._onResult.event; + private readonly _renderedHoverParts: MutableDisposable = this._register(new MutableDisposable()); + private readonly _renderedStatusBar: MutableDisposable = this._register(new MutableDisposable()); + constructor( private readonly _editor: ICodeEditor, private readonly _standaloneColorPickerVisible: IContextKey, @@ -168,22 +171,24 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW private _render(colorHover: StandaloneColorPickerHover, foundInEditor: boolean) { const fragment = document.createDocumentFragment(); - const statusBar = this._register(new EditorHoverStatusBar(this._keybindingService, this._hoverService)); + this._renderedStatusBar.value = this._register(new EditorHoverStatusBar(this._keybindingService, this._hoverService)); const context: IEditorHoverRenderContext = { fragment, - statusBar, + statusBar: this._renderedStatusBar.value, onContentsChanged: () => { }, - hide: () => this.hide() + setMinimumDimensions: () => { }, + hide: () => this.hide(), }; this._colorHover = colorHover; - const renderedHoverPart = this._standaloneColorPickerParticipant.renderHoverParts(context, [colorHover]); - if (!renderedHoverPart) { + this._renderedHoverParts.value = this._standaloneColorPickerParticipant.renderHoverParts(context, [colorHover]); + if (!this._renderedHoverParts.value) { + this._renderedStatusBar.clear(); + this._renderedHoverParts.clear(); return; } - this._register(renderedHoverPart.disposables); - const colorPicker = renderedHoverPart.colorPicker; + const colorPicker = this._renderedHoverParts.value.colorPicker; this._body.classList.add('standalone-colorpicker-body'); this._body.style.maxHeight = Math.max(this._editor.getLayoutInfo().height / 4, 250) + 'px'; this._body.style.maxWidth = Math.max(this._editor.getLayoutInfo().width * 0.66, 500) + 'px'; diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index e67fdbe9ff1..a82edb31852 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -103,7 +103,7 @@ export interface IEditorHoverContext { /** * Set the minimum dimensions of the resizable hover */ - setMinimumDimensions?(dimensions: Dimension): void; + setMinimumDimensions(dimensions: Dimension): void; /** * Hide the hover. */ From 0c7bdaaae9b654d97ee5b212bbf4b81bd871540f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Dec 2024 15:19:43 +0100 Subject: [PATCH 104/479] fix https://github.com/microsoft/vscode/issues/209764 (#235827) --- src/vs/editor/contrib/codelens/browser/codelensWidget.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codelens/browser/codelensWidget.css b/src/vs/editor/contrib/codelens/browser/codelensWidget.css index 14711df31af..ab6a97ab7bc 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensWidget.css +++ b/src/vs/editor/contrib/codelens/browser/codelensWidget.css @@ -22,6 +22,8 @@ -webkit-user-select: none; white-space: nowrap; vertical-align: sub; + display: inline-flex; + align-items: center; } .monaco-editor .codelens-decoration > a { @@ -37,7 +39,7 @@ color: var(--vscode-editorLink-activeForeground) !important; } -.monaco-editor .codelens-decoration .codicon { +.monaco-editor .codelens-decoration .codicon[class*='codicon-'] { vertical-align: middle; color: currentColor !important; color: var(--vscode-editorCodeLens-foreground); @@ -45,6 +47,7 @@ font-size: var(--vscode-editorCodeLens-fontSize); } + .monaco-editor .codelens-decoration > a:hover .codicon::before { cursor: pointer; } From 0011fcfed59d372e2ef0e4730afd05f20c73b22f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Dec 2024 16:06:07 +0100 Subject: [PATCH 105/479] prevent `null` from being used as language name (#235832) https://github.com/microsoft/vscode/issues/226053 --- .../contrib/snippets/browser/commands/configureSnippets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts index c579cace7e4..29f82cafa74 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts @@ -101,7 +101,7 @@ async function computePicks(snippetService: ISnippetsService, userDataProfileSer const mode = basename(file.location).replace(/\.json$/, ''); existing.push({ label: basename(file.location), - description: `(${languageService.getLanguageName(mode)})`, + description: `(${languageService.getLanguageName(mode) ?? mode})`, filepath: file.location }); seen.add(mode); From 20bed5838f77a7526429fb04dbe7eb7323c702da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 11 Dec 2024 16:19:41 +0100 Subject: [PATCH 106/479] fix python codicon size (#235833) --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 82588 -> 82548 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 01f7648ad16f1fe1a31451e092708832703edd89..f60b260e0402715b055fd642ceac4c8143bb9161 100644 GIT binary patch delta 861 zcmah_TS${(82;Yx|NevK+$x>RYSA1^RBSBgI>;Q+iHMxj&AH`a?clGQne*S8^RNgj z7!jI6VHOfy#4fsUB@rSL)Wzs3B%%x1MMQ*j5favg_RWjvsxID#?|q){dES?I<~qAJ z#*SM~)SR5ZiBNeJLL1LSzsKjiJKKa1%F?P8DS_P#eWC?gTCrD(X``Dz5rS2O_~uZb z*YhZpU6>ftXmwf2u3zpOkftilqX^1BAhc@+0cB}+4mUR^GHRl=j0Y!Hcfn?wgSTvY zgFJ@GvqUlZmAp(_@}BQaM9;r($Zwcx_-<{s-mt!J#EoL(y~ahG(q^z(Y`>a1o5q?p zn&Zt&cD-G+&)U}=I)}sYxy8~l)AGZqb{=;IoQuwtR@~}nooiinRk>`g8?GgHzFTxp zyI;81E?m9vwawR-X|HIXZU4}*qa)Oj?bLPN@sxV9o{hxCJ>Gn8o!91#d0+ViU%Aia zd+4wBTm4CY#{Vr)8mJ3|yM(T~F0t#W7!b$172W5$=et*WR6VzQGQq;2HFzudE~E}c zL(`#8y~DlBearpH{`vlo;ZxyD;py;PI6Gh%=p2}ntWr{%mQvD7DHExP^h8pTwdj$f z(NuK#iss6b*ol}FdlOq9tQkxWzKyHmhvTXEw;}ydaws+QWw>bg((tVj>&VjR#nF!m zb;6adDv#bzY~6Y{lp+JFM-g-lA(QTa&Sb`>T2t*YFqRp0H6}CA&I|@ME>S~KA?CSC z$l+cn)0k?F2ca5xPFbQflu+PvO72JTp<-nbUq~einreKY8jPkIGt>hOHstb+riAg3 zf#-_%ftrq>gEjh{0s}yJBIGf8eLVm$3|qj*A(TpnTNn!k;?qm!-6r%%7OkNgIV5#Sa8 delta 896 zcmah_Ur3Wt6ud*~A7pZJ&Lt^GWl3WrdQeso*ob&J8w>jt3oS$=U(}h@s zR47V>$q62Zo$$!X=OxGPeP(N5PF($WZ!E{istPj_}tN+$e+)&+MZj?7ljjzlK z^9%EKQ&m&AY2A`yDYtkmf1AzC@2xVc-fFQ1t>3K2Ey@;8%cc$3Ds3*?f^EB1-5PFP zZT-^5wavFB+5_!79mbCJj>Aq(=cCS(uJW$8_7eL^w<6Mg-Jy1v94^N*$9FMDyd@5Z zYtB1P(HV8_xR9&FWpX{~$>}lmgnK@@gYGxI1-)IpTfN5~ooCsz)0fvL_AT}8dG+3D z@2dB-f2MzbaDON|v^8|(v-u`{tG-R&$#C_sG`uN^QdC-%;?h@XXT&%%F%tK4{+o6F zxPO1tFuD=22A&3fjHQg#j77(GgSudCFdjmos!%i(5B&=lh9|>IXj-rB$g>hY+gRg zuyV*Uxv&^&NwETIOGv6zDGe$TNG%OAq=(68vs78i%=2av@-(E6SOqjjkWfly=+DI~ zU^!HgdhXc-!9ez4u4eHVLKm(FtO2$3SsASB07`Y7x=ZTCM`2f!Lt;_lSv$eD4XVZ%u*ynl$@h8%2Q!v^|45nU_5c6? From 2d47752d6b2b0f331eeb8fc09e6ffd28db62d344 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:47:43 +0100 Subject: [PATCH 107/479] Enhance drop feedback for empty panel (#235834) fix #99052 --- .../browser/parts/media/paneCompositePart.css | 10 +++++--- .../browser/parts/paneCompositePart.ts | 24 +++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index 854c02e6bd5..a57bdd617ce 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -20,17 +20,21 @@ display: none; } +.monaco-workbench .pane-composite-part.empty > .title.has-composite-bar > .title-label { + border-bottom: none !important; +} + .monaco-workbench .pane-composite-part > .header-or-footer { padding-left: 4px; padding-right: 4px; background-color: var(--vscode-activityBarTop-background); } -.monaco-workbench .pane-composite-part > .header { +.monaco-workbench .pane-composite-part:not(.empty) > .header { border-bottom: 1px solid var(--vscode-sideBarActivityBarTop-border); } -.monaco-workbench .pane-composite-part > .footer { +.monaco-workbench .pane-composite-part:not(.empty) > .footer { border-top: 1px solid var(--vscode-sideBarActivityBarTop-border); } @@ -325,7 +329,7 @@ width: 100%; } -.monaco-workbench .pane-composite-part .empty-pane-message-area.visible { +.monaco-workbench .pane-composite-part.empty .empty-pane-message-area { display: flex; align-items: center; align-content: center; diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index cfad7c02b95..c8780871219 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -275,6 +275,20 @@ export abstract class AbstractPaneCompositePart extends CompositePart { + const updateActivityBarBackground = !this.getActiveComposite() || !visible; + const backgroundColor = visible ? this.theme.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND)?.toString() || '' : ''; + + if (this.titleContainer && updateActivityBarBackground) { + this.titleContainer.style.backgroundColor = backgroundColor; + } + if (this.headerFooterCompositeBarContainer && updateActivityBarBackground) { + this.headerFooterCompositeBarContainer.style.backgroundColor = backgroundColor; + } + + this.emptyPaneMessageElement!.style.backgroundColor = backgroundColor; + }; + this._register(CompositeDragAndDropObserver.INSTANCE.registerTarget(this.element, { onDragOver: (e) => { EventHelper.stop(e.eventData, true); @@ -287,20 +301,20 @@ export abstract class AbstractPaneCompositePart extends CompositePart { EventHelper.stop(e.eventData, true); - this.emptyPaneMessageElement!.style.backgroundColor = ''; + setDropBackgroundFeedback(false); }, onDragEnd: (e) => { EventHelper.stop(e.eventData, true); - this.emptyPaneMessageElement!.style.backgroundColor = ''; + setDropBackgroundFeedback(false); }, onDrop: (e) => { EventHelper.stop(e.eventData, true); - this.emptyPaneMessageElement!.style.backgroundColor = ''; + setDropBackgroundFeedback(false); if (this.paneCompositeBar.value) { this.paneCompositeBar.value.dndHandler.drop(e.dragAndDropData, undefined, e.eventData); } else { @@ -596,7 +610,7 @@ export abstract class AbstractPaneCompositePart extends CompositePart Date: Wed, 11 Dec 2024 17:05:06 +0100 Subject: [PATCH 108/479] when only one hover part, focus that hover part (#235829) --- .../editor/contrib/hover/browser/contentHoverRendered.ts | 8 ++++++++ .../contrib/hover/browser/contentHoverWidgetWrapper.ts | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index c72ecfb5910..dde3cb77bb6 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -79,6 +79,10 @@ export class RenderedContentHover extends Disposable { return this._renderedHoverParts.focusedHoverPartIndex; } + public get hoverPartsCount(): number { + return this._renderedHoverParts.hoverPartsCount; + } + public focusHoverPartWithIndex(index: number): void { this._renderedHoverParts.focusHoverPartWithIndex(index); } @@ -436,4 +440,8 @@ class RenderedContentHoverParts extends Disposable { public get focusedHoverPartIndex(): number { return this._focusedHoverPartIndex; } + + public get hoverPartsCount(): number { + return this._renderedParts.length; + } } diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts index 5beccabb33b..cd2fedb338d 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts @@ -329,6 +329,11 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge } public focus(): void { + const hoverPartsCount = this._renderedContentHover?.hoverPartsCount; + if (hoverPartsCount === 1) { + this.focusHoverPartWithIndex(0); + return; + } this._contentHoverWidget.focus(); } From d9cf59b66d040d45cc8cc7a2d1de3134f5f4b0c0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 17:41:51 +0100 Subject: [PATCH 109/479] Not clear how to add additional reactions to PR comments (#235828) Fixes #133601 --- src/vs/workbench/contrib/comments/browser/commentNode.ts | 8 ++------ .../workbench/contrib/comments/browser/media/review.css | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index a8598ae91e4..37958bce7d8 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -145,9 +145,7 @@ export class CommentNode extends Disposable { this.createScroll(this._commentDetailsContainer, this._body); this.updateCommentBody(this.comment.body); - if (this.comment.commentReactions && this.comment.commentReactions.length && this.comment.commentReactions.filter(reaction => !!reaction.count).length) { - this.createReactionsContainer(this._commentDetailsContainer); - } + this.createReactionsContainer(this._commentDetailsContainer); this._domNode.setAttribute('aria-label', `${comment.userName}, ${this.commentBodyValue}`); this._domNode.setAttribute('role', 'treeitem'); @@ -744,9 +742,7 @@ export class CommentNode extends Disposable { this._reactionsActionBar?.clear(); - if (this.comment.commentReactions && this.comment.commentReactions.some(reaction => !!reaction.count)) { - this.createReactionsContainer(this._commentDetailsContainer); - } + this.createReactionsContainer(this._commentDetailsContainer); if (this.comment.contextValue) { this._commentContextValue.set(this.comment.contextValue); diff --git a/src/vs/workbench/contrib/comments/browser/media/review.css b/src/vs/workbench/contrib/comments/browser/media/review.css index 7ffd420fbd3..e062349f9de 100644 --- a/src/vs/workbench/contrib/comments/browser/media/review.css +++ b/src/vs/workbench/contrib/comments/browser/media/review.css @@ -183,7 +183,7 @@ border: none; } -.review-widget .body .review-comment .review-comment-contents .comment-reactions:hover .action-item a.action-label.toolbar-toggle-pickReactions { +.review-widget .body .review-comment .review-comment-contents:hover .comment-reactions .action-item a.action-label.toolbar-toggle-pickReactions { display: inline-block; background-size: 16px; } From 09fe3fcbc1514bc35742dc4325d05b7d7adfcad2 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 11 Dec 2024 17:53:04 +0100 Subject: [PATCH 110/479] Adopt new Ruby grammar (#235732) --- extensions/ruby/cgmanifest.json | 57 +- extensions/ruby/package.json | 2 +- extensions/ruby/syntaxes/ruby.tmLanguage.json | 4540 +++++++++-------- .../theme-defaults/themes/dark_plus.json | 3 +- .../theme-defaults/themes/hc_black.json | 3 +- .../theme-defaults/themes/hc_light.json | 3 +- .../theme-defaults/themes/light_plus.json | 3 +- .../themes/kimbie-dark-color-theme.json | 3 +- .../themes/dimmed-monokai-color-theme.json | 5 +- .../themes/monokai-color-theme.json | 5 +- .../themes/quietlight-color-theme.json | 1 + .../theme-red/themes/Red-color-theme.json | 7 +- .../themes/solarized-dark-color-theme.json | 5 +- .../themes/solarized-light-color-theme.json | 5 +- .../tomorrow-night-blue-color-theme.json | 2 +- .../test/colorize-results/test_rb.json | 1002 +++- 16 files changed, 3298 insertions(+), 2348 deletions(-) diff --git a/extensions/ruby/cgmanifest.json b/extensions/ruby/cgmanifest.json index 2cd9595e4e5..49719a8afe9 100644 --- a/extensions/ruby/cgmanifest.json +++ b/extensions/ruby/cgmanifest.json @@ -4,27 +4,56 @@ "component": { "type": "git", "git": { - "name": "textmate/ruby.tmbundle", - "repositoryUrl": "https://github.com/textmate/ruby.tmbundle", - "commitHash": "efcb8941c701343f1b2e9fb105c678152fea6892" + "name": "Shopify/ruby-lsp", + "repositoryUrl": "https://github.com/Shopify/ruby-lsp", + "commitHash": "01a8ecf608b7d8607adcd89c32db72ae3852f33b" } }, "licenseDetail": [ - "Copyright (c) textmate-ruby.tmbundle project authors", + "The MIT License (MIT)", "", - "If not otherwise specified (see below), files in this folder fall under the following license: ", + "Copyright (c) 2022-present, Shopify Inc.", "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", "", - "An exception is made for files in readable text which contain their own license information, ", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", - "\"tidy\" is accompanied by \"tidy-license.txt\"." + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", + "THE SOFTWARE.", + "", + "================================================================================", + "The following files and related configuration in package.json are based on a", + "sequence of adaptions: grammars/ruby.cson.json, grammars/erb.cson.json,", + "languages/erb.json.", + "", + "Copyright (c) 2016 Peng Lv", + "Copyright (c) 2017-2019 Stafford Brunk", + "https://github.com/rubyide/vscode-ruby", + "", + " Released under the MIT license", + " https://github.com/rubyide/vscode-ruby/blob/main/LICENSE.txt", + "", + "Copyright (c) 2014 GitHub Inc.", + "https://github.com/atom/language-ruby", + "", + " Released under the MIT license", + " https://github.com/atom/language-ruby/blob/master/LICENSE.md", + "", + "https://github.com/textmate/ruby.tmbundle", + " https://github.com/textmate/ruby.tmbundle#license" ], - "license": "TextMate Bundle License", + "license": "MIT License", "version": "0.0.0" } ], diff --git a/extensions/ruby/package.json b/extensions/ruby/package.json index 8210a7825af..8dc105b3481 100644 --- a/extensions/ruby/package.json +++ b/extensions/ruby/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/ruby.tmbundle Syntaxes/Ruby.plist ./syntaxes/ruby.tmLanguage.json" + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin Shopify/ruby-lsp vscode/grammars/ruby.cson.json ./syntaxes/ruby.tmLanguage.json" }, "categories": ["Programming Languages"], "contributes": { diff --git a/extensions/ruby/syntaxes/ruby.tmLanguage.json b/extensions/ruby/syntaxes/ruby.tmLanguage.json index 84d619cc51a..84fb37228e6 100644 --- a/extensions/ruby/syntaxes/ruby.tmLanguage.json +++ b/extensions/ruby/syntaxes/ruby.tmLanguage.json @@ -1,13 +1,12 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/textmate/ruby.tmbundle/blob/master/Syntaxes/Ruby.plist", + "This file has been converted from https://github.com/Shopify/ruby-lsp/blob/master/vscode/grammars/ruby.cson.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/ruby.tmbundle/commit/efcb8941c701343f1b2e9fb105c678152fea6892", + "version": "https://github.com/Shopify/ruby-lsp/commit/01a8ecf608b7d8607adcd89c32db72ae3852f33b", "name": "Ruby", "scopeName": "source.ruby", - "comment": "\n\tTODO: unresolved issues\n\n\ttext:\n\t\"p <>)=)", + "comment": "A local variable operation assignment (+=, -=, *=, /=)" + }, + { + "captures": { + "1": { + "name": "keyword.control.ruby" }, - "7": { - "name": "entity.other.inherited-class.module.third.ruby" + "3": { + "name": "variable.ruby" }, - "8": { - "name": "punctuation.separator.inheritance.ruby" + "5": { + "name": "keyword.operator.assignment.augmented.ruby" } }, - "match": "^\\s*(module)\\s+(([A-Z]\\w*(::))?([A-Z]\\w*(::))?([A-Z]\\w*(::))*[A-Z]\\w*)", - "name": "meta.module.ruby" + "match": "(?>)=)", + "comment": "A local variable operation assignment in a condition" }, { - "comment": "else if is a common mistake carried over from other languages. it works if you put in a second end, but it’s never what you want.", - "match": "(?]", + "comment": "A local variable assignment" }, { "captures": { "1": { - "name": "punctuation.definition.constant.ruby" + "name": "keyword.control.ruby" + }, + "3": { + "name": "variable.ruby" + } + }, + "match": "(?]", + "comment": "A local variable assignment in a condition" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.constant.hashkey.ruby" } }, "comment": "symbols as hash key (1.9 syntax)", "match": "(?>[a-zA-Z_]\\w*(?>[?!])?)(:)(?!:)", - "name": "constant.other.symbol.hashkey.ruby" + "name": "constant.language.symbol.hashkey.ruby" }, { "captures": { @@ -86,7 +158,7 @@ }, "comment": "symbols as hash key (1.8 syntax)", "match": "(?[a-zA-Z_]\\w*(?>[?!])?)(?=\\s*=>)", - "name": "constant.other.symbol.hashkey.ruby" + "name": "constant.language.symbol.hashkey.ruby" }, { "comment": "everything being a reserved word, not a value and needing a 'end' is a..", @@ -100,61 +172,42 @@ }, { "comment": "contextual smart pair support", - "match": "(?<=\\{)(\\s+)", + "match": "(?<={)(\\s+)", "name": "meta.syntax.ruby.start-block" }, { - "match": "(?|_|\\*|\\$|\\?|:|\"|-[0adFiIlpvw])", + "match": "(\\$)(!|@|&|`|'|\\+|\\d+|~|=|/|\\\\|,|;|\\.|<|>|_|\\*|\\$|\\?|:|\"|-[0adFiIlpv])", "name": "variable.other.readwrite.global.pre-defined.ruby" }, { @@ -205,7 +258,7 @@ "name": "variable.other.constant.ruby" } }, - "end": "\\]", + "end": "]", "name": "meta.environment-variable.ruby", "patterns": [ { @@ -218,15 +271,39 @@ "name": "support.class.ruby" }, { - "match": "\\b(abort|at_exit|autoload[?]?|binding|callcc|caller|caller_locations|chomp|chop|eval|exec|exit|exit!|fork|format|gets|global_variables|gsub|lambda|load|local_variables|open|p|print|printf|proc|putc|puts|rand|readline|readlines|select|set_trace_func|sleep|spawn|sprintf|srand|sub|syscall|system|test|trace_var|trap|untrace_var|warn)(\\b|(?<=[?!]))(?![?!])", + "match": "\\b((abort|at_exit|autoload|binding|callcc|caller|caller_locations|chomp|chop|eval|exec|exit|fork|format|gets|global_variables|gsub|lambda|load|local_variables|open|p|print|printf|proc|putc|puts|rand|readline|readlines|select|set_trace_func|sleep|spawn|sprintf|srand|sub|syscall|system|test|trace_var|trap|untrace_var|warn)\\b(?![?!])|autoload\\?|exit!)", "name": "support.function.kernel.ruby" }, { - "match": "\\b[A-Z]\\w*\\b", + "match": "\\b[_A-Z]\\w*\\b", "name": "variable.other.constant.ruby" }, { - "begin": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\s+ # the def keyword\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) # …or an operator method\n\t\t\t \\s*(\\() # the openning parenthesis for arguments\n\t\t\t ", + "begin": "(->)\\(", + "beginCaptures": { + "1": { + "name": "support.function.kernel.ruby" + } + }, + "comment": "Lambda parameters.", + "end": "\\)", + "patterns": [ + { + "begin": "(?=[&*_a-zA-Z])", + "end": "(?=[,)])", + "patterns": [ + { + "include": "#method_parameters" + } + ] + }, + { + "include": "#method_parameters" + } + ] + }, + { + "begin": "(?x)\n(?=def\\b) # optimization to help Oniguruma fail fast\n(?<=^|\\s)(def)\\s+\n(\n (?>[a-zA-Z_]\\w*(?>\\.|::))? # method prefix\n (?> # method name\n [a-zA-Z_]\\w*(?>[?!]|=(?!>))?\n |\n ===?|!=|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n )\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "keyword.control.def.ruby" @@ -238,7 +315,7 @@ "name": "punctuation.definition.parameters.ruby" } }, - "comment": "the method pattern comes from the symbol pattern, see there for a explaination", + "comment": "The method pattern comes from the symbol pattern. See there for an explanation.", "end": "\\)", "endCaptures": { "0": { @@ -252,89 +329,17 @@ "end": "(?=[,)])", "patterns": [ { - "captures": { - "1": { - "name": "storage.type.variable.ruby" - }, - "2": { - "name": "constant.other.symbol.hashkey.parameter.function.ruby" - }, - "3": { - "name": "punctuation.definition.constant.ruby" - }, - "4": { - "name": "variable.parameter.function.ruby" - } - }, - "match": "\\G([&*]?)(?:([_a-zA-Z]\\w*(:))|([_a-zA-Z]\\w*))" - }, - { - "include": "#parens" - }, - { - "include": "#braces" - }, - { - "include": "$self" - } - ] - } - ], - "repository": { - "braces": { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.section.function.begin.ruby" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.section.function.end.ruby" - } - }, - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#braces" - }, - { - "include": "$self" + "include": "#method_parameters" } ] }, - "parens": { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.section.function.begin.ruby" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.section.function.end.ruby" - } - }, - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#braces" - }, - { - "include": "$self" - } - ] + { + "include": "#method_parameters" } - } + ] }, { - "begin": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\s+ # the def keyword\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) # …or an operator method\n\t\t\t [ \\t] # the space separating the arguments\n\t\t\t (?=[ \\t]*[^\\s#;]) # make sure arguments and not a comment follow\n\t\t\t ", + "begin": "(?x)\n(?=def\\b) # optimization to help Oniguruma fail fast\n(?<=^|\\s)(def)\\s+\n(\n (?>[a-zA-Z_]\\w*(?>\\.|::))? # method prefix\n (?> # method name\n [a-zA-Z_]\\w*(?>[?!]|=(?!>))?\n |\n ===?|!=|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n )\n)\n[ \\t]\n(?=[ \\t]*[^\\s#;]) # make sure the following is not comment", "beginCaptures": { "1": { "name": "keyword.control.def.ruby" @@ -344,35 +349,20 @@ } }, "comment": "same as the previous rule, but without parentheses around the arguments", - "end": "$", + "end": "(?=;)|(?<=[\\w\\])}`'\"!?])(?=\\s*#|\\s*$)", "name": "meta.function.method.with-arguments.ruby", "patterns": [ { - "begin": "(?![\\s,])", - "end": "(?=,|$)", + "begin": "(?=[&*_a-zA-Z])", + "end": "(?=,|;|\\s*#|\\s*$)", "patterns": [ { - "captures": { - "1": { - "name": "storage.type.variable.ruby" - }, - "2": { - "name": "constant.other.symbol.hashkey.parameter.function.ruby" - }, - "3": { - "name": "punctuation.definition.constant.ruby" - }, - "4": { - "name": "variable.parameter.function.ruby" - } - }, - "match": "\\G([&*]?)(?:([_a-zA-Z]\\w*(:))|([_a-zA-Z]\\w*))", - "name": "variable.parameter.function.ruby" - }, - { - "include": "$self" + "include": "#method_parameters" } ] + }, + { + "include": "#method_parameters" } ] }, @@ -386,38 +376,28 @@ } }, "comment": " the optional name is just to catch the def also without a method-name", - "match": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\b # the def keyword\n\t\t\t ( \\s+ # an optional group of whitespace followed by…\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) )? # …or an operator method\n\t\t\t ", + "match": "(?x)\n(?=def\\b) # optimization to help Oniguruma fail fast\n(?<=^|\\s)(def)\\b\n(\n \\s+\n (\n (?>[a-zA-Z_]\\w*(?>\\.|::))? # method prefix\n (?> # method name\n [a-zA-Z_]\\w*(?>[?!]|=(?!>))?\n |\n ===?|!=|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n )\n )\n)?", "name": "meta.function.method.without-arguments.ruby" }, { - "match": "\\b\\d(?>_?\\d)*(?=\\.\\d|[eE])(\\.\\d(?>_?\\d)*)?([eE][-+]?\\d(?>_?\\d)*)?r?i?\\b", - "name": "constant.numeric.float.ruby" - }, - { - "match": "\\b(0|(0[dD]\\d|[1-9])(?>_?\\d)*)r?i?\\b", - "name": "constant.numeric.integer.ruby" - }, - { - "match": "\\b0[xX]\\h(?>_?\\h)*r?i?\\b", - "name": "constant.numeric.hex.ruby" - }, - { - "match": "\\b0[bB][01](?>_?[01])*r?i?\\b", - "name": "constant.numeric.binary.ruby" - }, - { - "match": "\\b0([oO]?[0-7](?>_?[0-7])*)?r?i?\\b", - "name": "constant.numeric.octal.ruby" + "match": "(?x)\n\\b\n(\n [\\d](?>_?\\d)* # 100_000\n (\\.(?![^[:space:][:digit:]])(?>_?\\d)*)? # fractional part\n ([eE][-+]?\\d(?>_?\\d)*)? # 1.23e-4\n |\n 0\n (?:\n [xX]\\h(?>_?\\h)*|\n [oO]?[0-7](?>_?[0-7])*|\n [bB][01](?>_?[01])*|\n [dD]\\d(?>_?\\d)*\n ) # A base indicator can only be used with an integer\n)\\b", + "name": "constant.numeric.ruby" }, { "begin": ":'", - "captures": { + "beginCaptures": { "0": { - "name": "punctuation.definition.constant.ruby" + "name": "punctuation.definition.symbol.begin.ruby" } }, + "comment": "symbol literal with '' delimiter", "end": "'", - "name": "constant.other.symbol.single-quoted.ruby", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { "match": "\\\\['\\\\]", @@ -427,13 +407,19 @@ }, { "begin": ":\"", - "captures": { + "beginCaptures": { "0": { - "name": "punctuation.definition.constant.ruby" + "name": "punctuation.section.symbol.begin.ruby" } }, + "comment": "symbol literal with \"\" delimiter", "end": "\"", - "name": "constant.other.symbol.double-quoted.ruby", + "endCaptures": { + "0": { + "name": "punctuation.section.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { "include": "#interpolated_ruby" @@ -455,7 +441,7 @@ "name": "punctuation.definition.string.begin.ruby" } }, - "comment": "single quoted string (does not allow interpolation)", + "comment": "string literal with '' delimiter", "end": "'", "endCaptures": { "0": { @@ -477,14 +463,14 @@ "name": "punctuation.definition.string.begin.ruby" } }, - "comment": "double quoted string (allows for interpolation)", + "comment": "string literal with interpolation and \"\" delimiter", "end": "\"", "endCaptures": { "0": { "name": "punctuation.definition.string.end.ruby" } }, - "name": "string.quoted.double.ruby", + "name": "string.quoted.double.interpolated.ruby", "patterns": [ { "include": "#interpolated_ruby" @@ -495,7 +481,7 @@ ] }, { - "begin": "`", + "begin": "(?~(?:\\[,|&;]\n\t\t\t | [\\s;]if\\s\t\t\t# keywords\n\t\t\t | [\\s;]elsif\\s\n\t\t\t | [\\s;]while\\s\n\t\t\t | [\\s;]unless\\s\n\t\t\t | [\\s;]when\\s\n\t\t\t | [\\s;]assert_match\\s\n\t\t\t | [\\s;]or\\s\t\t\t# boolean opperators\n\t\t\t | [\\s;]and\\s\n\t\t\t | [\\s;]not\\s\n\t\t\t | [\\s.]index\\s\t\t\t# methods\n\t\t\t | [\\s.]scan\\s\n\t\t\t | [\\s.]sub\\s\n\t\t\t | [\\s.]sub!\\s\n\t\t\t | [\\s.]gsub\\s\n\t\t\t | [\\s.]gsub!\\s\n\t\t\t | [\\s.]match\\s\n\t\t\t )\n\t\t\t | (?<= # or a look-behind with line anchor:\n\t\t\t ^when\\s # duplication necessary due to limits of regex\n\t\t\t | ^if\\s\n\t\t\t | ^elsif\\s\n\t\t\t | ^while\\s\n\t\t\t | ^unless\\s\n\t\t\t )\n\t\t\t )\n\t\t\t \\s*((/))(?![*+{}?])\n\t\t\t", + "begin": "(?x)\n(?|=>|==|=~|!~|!=|;|$|\n if|else|elsif|then|do|end|unless|while|until|or|and\n )\n |\n $\n)", "captures": { "1": { - "name": "string.regexp.classic.ruby" + "name": "string.regexp.interpolated.ruby" }, "2": { - "name": "punctuation.definition.string.ruby" + "name": "punctuation.section.regexp.ruby" } }, - "comment": "regular expressions (normal)\n\t\t\twe only start a regexp if the character before it (excluding whitespace)\n\t\t\tis what we think is before a regexp\n\t\t\t", - "contentName": "string.regexp.classic.ruby", + "comment": "regular expression literal with interpolation", + "contentName": "string.regexp.interpolated.ruby", "end": "((/[eimnosux]*))", "patterns": [ { @@ -541,2145 +524,2272 @@ ] }, { - "captures": { - "1": { - "name": "punctuation.definition.constant.ruby" + "begin": "%r{", + "beginCaptures": { + "0": { + "name": "punctuation.section.regexp.begin.ruby" } }, - "comment": "symbols", - "match": "(?[a-zA-Z_]\\w*(?>[?!]|=(?![>=]))?|===?|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[\\]=?|(@@?|\\$)[a-zA-Z_]\\w*)", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "^=begin", - "captures": { + "end": "}[eimnosux]*", + "endCaptures": { "0": { - "name": "punctuation.definition.comment.ruby" + "name": "punctuation.section.regexp.end.ruby" } }, - "comment": "multiline comments", - "end": "^=end", - "name": "comment.block.documentation.ruby" - }, + "name": "string.regexp.interpolated.ruby", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_curly_r" + } + ] + }, { - "begin": "(^[ \\t]+)?(?=#)", + "begin": "%r\\[", "beginCaptures": { - "1": { - "name": "punctuation.whitespace.comment.leading.ruby" + "0": { + "name": "punctuation.section.regexp.begin.ruby" } }, - "end": "(?!\\G)", + "end": "][eimnosux]*", + "endCaptures": { + "0": { + "name": "punctuation.section.regexp.end.ruby" + } + }, + "name": "string.regexp.interpolated.ruby", "patterns": [ { - "begin": "#", - "beginCaptures": { - "0": { - "name": "punctuation.definition.comment.ruby" - } - }, - "end": "\\n", - "name": "comment.line.number-sign.ruby" + "include": "#regex_sub" + }, + { + "include": "#nest_brackets_r" } ] }, { - "comment": "\n\t\t\tmatches questionmark-letters.\n\n\t\t\texamples (1st alternation = hex):\n\t\t\t?\\x1 ?\\x61\n\n\t\t\texamples (2nd alternation = octal):\n\t\t\t?\\0 ?\\07 ?\\017\n\n\t\t\texamples (3rd alternation = escaped):\n\t\t\t?\\n ?\\b\n\n\t\t\texamples (4th alternation = meta-ctrl):\n\t\t\t?\\C-a ?\\M-a ?\\C-\\M-\\C-\\M-a\n\n\t\t\texamples (4th alternation = normal):\n\t\t\t?a ?A ?0 \n\t\t\t?* ?\" ?( \n\t\t\t?. ?#\n\t\t\t\n\t\t\t\n\t\t\tthe negative lookbehind prevents against matching\n\t\t\tp(42.tainted?)\n\t\t\t", - "match": "(?<<[-~](\"?)((?:[_\\w]+_|)HTML)\\b\\1))", - "comment": "Heredoc with embedded html", - "end": "(?!\\G)", - "name": "meta.embedded.block.html", + "begin": "%r<", + "beginCaptures": { + "0": { + "name": "punctuation.section.regexp.begin.ruby" + } + }, + "end": ">[eimnosux]*", + "endCaptures": { + "0": { + "name": "punctuation.section.regexp.end.ruby" + } + }, + "name": "string.regexp.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)HTML)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "text.html", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "text.html.basic" - }, - { - "include": "#escaped_char" - } - ] + "include": "#regex_sub" + }, + { + "include": "#nest_ltgt_r" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)XML)\\b\\1))", - "comment": "Heredoc with embedded xml", - "end": "(?!\\G)", - "name": "meta.embedded.block.xml", + "begin": "%r([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.regexp.begin.ruby" + } + }, + "end": "\\1[eimnosux]*", + "endCaptures": { + "0": { + "name": "punctuation.section.regexp.end.ruby" + } + }, + "name": "string.regexp.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)XML)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "text.xml", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "text.xml" - }, - { - "include": "#escaped_char" - } - ] + "include": "#regex_sub" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)SQL)\\b\\1))", - "comment": "Heredoc with embedded sql", - "end": "(?!\\G)", - "name": "meta.embedded.block.sql", + "begin": "%I\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)SQL)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.sql", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.sql" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_brackets_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)CSS)\\b\\1))", - "comment": "Heredoc with embedded css", - "end": "(?!\\G)", - "name": "meta.embedded.block.css", + "begin": "%I\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)CSS)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.css", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.css" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)CPP)\\b\\1))", - "comment": "Heredoc with embedded c++", - "end": "(?!\\G)", - "name": "meta.embedded.block.c++", + "begin": "%I<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)CPP)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.c++", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.c++" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)C)\\b\\1))", - "comment": "Heredoc with embedded c", - "end": "(?!\\G)", - "name": "meta.embedded.block.c", + "begin": "%I{", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)C)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.c", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.c" - }, - { - "include": "#escaped_char" - } - ] - } - ] - }, - { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1))", - "comment": "Heredoc with embedded javascript", - "end": "(?!\\G)", - "name": "meta.embedded.block.js", - "patterns": [ + "include": "#interpolated_ruby" + }, { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.js", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.js" - }, - { - "include": "#escaped_char" - } - ] + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)JQUERY)\\b\\1))", - "comment": "Heredoc with embedded jQuery javascript", - "end": "(?!\\G)", - "name": "meta.embedded.block.js.jquery", + "begin": "%I([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)JQUERY)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.js.jquery", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.js.jquery" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1))", - "comment": "Heredoc with embedded shell", - "end": "(?!\\G)", - "name": "meta.embedded.block.shell", + "begin": "%i\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.shell", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.shell" - }, - { - "include": "#escaped_char" - } - ] + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)LUA)\\b\\1))", - "comment": "Heredoc with embedded lua", - "end": "(?!\\G)", - "name": "meta.embedded.block.lua", + "begin": "%i\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)LUA)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.lua", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.lua" - }, - { - "include": "#escaped_char" - } - ] + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)RUBY)\\b\\1))", - "comment": "Heredoc with embedded ruby", - "end": "(?!\\G)", - "name": "meta.embedded.block.ruby", + "begin": "%i<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)RUBY)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.ruby", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.ruby" - }, - { - "include": "#escaped_char" - } - ] + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_ltgt" } ] }, { - "begin": "(?>=\\s*<<(\\w+))", + "begin": "%i{", "beginCaptures": { "0": { - "name": "punctuation.definition.string.begin.ruby" + "name": "punctuation.section.array.begin.ruby" } }, - "end": "^\\1$", + "end": "}", "endCaptures": { "0": { - "name": "punctuation.definition.string.end.ruby" + "name": "punctuation.section.array.end.ruby" } }, - "name": "string.unquoted.heredoc.ruby", + "name": "constant.language.symbol.ruby", "patterns": [ { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" }, { - "include": "#escaped_char" + "include": "#nest_curly" } ] }, { - "begin": "(?><<[-~](\\w+))", + "begin": "%i([^\\w])", "beginCaptures": { "0": { - "name": "punctuation.definition.string.begin.ruby" + "name": "punctuation.section.array.begin.ruby" } }, - "comment": "heredoc with indented terminator", - "end": "\\s*\\1$", + "end": "\\1", "endCaptures": { "0": { - "name": "punctuation.definition.string.end.ruby" + "name": "punctuation.section.array.end.ruby" } }, - "name": "string.unquoted.heredoc.ruby", + "name": "constant.language.symbol.ruby", "patterns": [ { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "#escaped_char" + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." } ] }, { - "begin": "(?<=\\{|do|\\{\\s|do\\s)(\\|)", - "captures": { - "1": { - "name": "punctuation.separator.arguments.ruby" + "begin": "%W\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" } }, - "end": "(?", - "name": "punctuation.separator.key-value" - }, - { - "match": "->", - "name": "support.function.kernel.lambda.ruby" - }, - { - "match": "<<=|%=|&{1,2}=|\\*=|\\*\\*=|\\+=|-=|\\^=|\\|{1,2}=|<<", - "name": "keyword.operator.assignment.augmented.ruby" - }, - { - "match": "<=>|<(?!<|=)|>(?!<|=|>)|<=|>=|===|==|=~|!=|!~|(?<=[ \\t])\\?", - "name": "keyword.operator.comparison.ruby" - }, - { - "match": "(?>", - "name": "keyword.operator.other.ruby" - }, - { - "match": ";", - "name": "punctuation.separator.statement.ruby" - }, - { - "match": ",", - "name": "punctuation.separator.object.ruby" - }, - { - "captures": { - "1": { - "name": "punctuation.separator.namespace.ruby" + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" } }, - "comment": "Mark as namespace separator if double colons followed by capital letter", - "match": "(::)\\s*(?=[A-Z])" + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" + } + ] }, { - "captures": { - "1": { - "name": "punctuation.separator.method.ruby" + "begin": "%W<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" } }, - "comment": "Mark as method separator if double colons not followed by capital letter", - "match": "(\\.|::)\\s*(?![A-Z])" - }, - { - "comment": "Must come after method and constant separators to prefer double colons", - "match": ":", - "name": "punctuation.separator.other.ruby" - }, - { - "match": "\\{", - "name": "punctuation.section.scope.begin.ruby" + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" + } + ] }, { - "match": "\\}", - "name": "punctuation.section.scope.end.ruby" + "begin": "%W{", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" + } + ] }, { - "match": "\\[", - "name": "punctuation.section.array.begin.ruby" + "begin": "%W([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] }, { - "match": "\\]", - "name": "punctuation.section.array.end.ruby" + "begin": "%w\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" + } + ] }, { - "match": "\\(|\\)", - "name": "punctuation.section.function.ruby" - } - ], - "repository": { - "escaped_char": { - "match": "\\\\(?:[0-7]{1,3}|x[\\da-fA-F]{1,2}|.)", - "name": "constant.character.escape.ruby" - }, - "heredoc": { - "begin": "^<<[-~]?\\w+", - "end": "$", + "begin": "%w\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", "patterns": [ { - "include": "$self" + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" } ] }, - "interpolated_ruby": { + { + "begin": "%w<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", "patterns": [ { - "begin": "#\\{", - "beginCaptures": { - "0": { - "name": "punctuation.section.embedded.begin.ruby" - } - }, - "contentName": "source.ruby", - "end": "(\\})", - "endCaptures": { - "0": { - "name": "punctuation.section.embedded.end.ruby" - }, - "1": { - "name": "source.ruby" - } - }, - "name": "meta.embedded.line.ruby", - "patterns": [ - { - "include": "#nest_curly_and_self" - }, - { - "include": "$self" - } - ], - "repository": { - "nest_curly_and_self": { - "patterns": [ - { - "begin": "\\{", - "captures": { - "0": { - "name": "punctuation.section.scope.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#nest_curly_and_self" - } - ] - }, - { - "include": "$self" - } - ] - } - } + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" }, { - "captures": { - "1": { - "name": "punctuation.definition.variable.ruby" - } - }, - "match": "(#@)[a-zA-Z_]\\w*", - "name": "variable.other.readwrite.instance.ruby" - }, + "include": "#nest_ltgt" + } + ] + }, + { + "begin": "%w{", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ { - "captures": { - "1": { - "name": "punctuation.definition.variable.ruby" - } - }, - "match": "(#@@)[a-zA-Z_]\\w*", - "name": "variable.other.readwrite.class.ruby" + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" }, { - "captures": { - "1": { - "name": "punctuation.definition.variable.ruby" - } - }, - "match": "(#\\$)[a-zA-Z_]\\w*", - "name": "variable.other.readwrite.global.ruby" + "include": "#nest_curly" } ] }, - "percent_literals": { + { + "begin": "%w([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", "patterns": [ { - "begin": "%i(?:([(\\[{<])|([^\\w\\s]|_))", - "beginCaptures": { - "0": { - "name": "punctuation.section.array.begin.ruby" - } - }, - "end": "[)\\]}>]\\2|\\1\\2", + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." + } + ] + }, + { + "begin": "%[Qx]?\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" + } + ] + }, + { + "begin": "%[Qx]?\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_brackets_i" + } + ] + }, + { + "begin": "%[Qx]?{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" + } + ] + }, + { + "begin": "%[Qx]?<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" + } + ] + }, + { + "begin": "%[Qx]([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "%([^\\w\\s=])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "%q\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" + } + ] + }, + { + "begin": "%q<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_ltgt" + } + ] + }, + { + "begin": "%q\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" + } + ] + }, + { + "begin": "%q{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_curly" + } + ] + }, + { + "begin": "%q([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." + } + ] + }, + { + "begin": "%s\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" + } + ] + }, + { + "begin": "%s<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_ltgt" + } + ] + }, + { + "begin": "%s\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" + } + ] + }, + { + "begin": "%s{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_curly" + } + ] + }, + { + "begin": "%s([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." + } + ] + }, + { + "captures": { + "1": { + "name": "punctuation.definition.constant.ruby" + } + }, + "comment": "symbols", + "match": "(?x)\n(?\n [$a-zA-Z_]\\w*(?>[?!]|=(?![>=]))?\n |\n ===?|<=>|>[>=]?|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n |\n @@?[a-zA-Z_]\\w*\n)", + "name": "constant.language.symbol.ruby" + }, + { + "begin": "^=begin", + "captures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, + "comment": "multiline comments", + "end": "^=end", + "name": "comment.block.documentation.ruby" + }, + { + "include": "#yard" + }, + { + "begin": "(^[ \\t]+)?(?=#)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.ruby" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.ruby" + } + ] + }, + { + "comment": "\n\t\t\tmatches questionmark-letters.\n\n\t\t\texamples (1st alternation = hex):\n\t\t\t?\\x1 ?\\x61\n\n\t\t\texamples (2nd alternation = octal):\n\t\t\t?\\0 ?\\07 ?\\017\n\n\t\t\texamples (3rd alternation = escaped):\n\t\t\t?\\n ?\\b\n\n\t\t\texamples (4th alternation = meta-ctrl):\n\t\t\t?\\C-a ?\\M-a ?\\C-\\M-\\C-\\M-a\n\n\t\t\texamples (4th alternation = normal):\n\t\t\t?a ?A ?0 \n\t\t\t?* ?\" ?( \n\t\t\t?. ?#\n\t\t\t\n\t\t\t\n\t\t\tthe negative lookbehind prevents against matching\n\t\t\tp(42.tainted?)\n\t\t\t", + "match": "(?<<[-~]([\"'`]?)((?:[_\\w]+_|)HTML)\\b\\1))", + "comment": "Heredoc with embedded HTML", + "end": "(?!\\G)", + "name": "meta.embedded.block.html", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)HTML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.html", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.html.basic" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)HAML)\\b\\1))", + "comment": "Heredoc with embedded HAML", + "end": "(?!\\G)", + "name": "meta.embedded.block.haml", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)HAML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.haml", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.haml" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)XML)\\b\\1))", + "comment": "Heredoc with embedded XML", + "end": "(?!\\G)", + "name": "meta.embedded.block.xml", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)XML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.xml", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.xml" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SQL)\\b\\1))", + "comment": "Heredoc with embedded SQL", + "end": "(?!\\G)", + "name": "meta.embedded.block.sql", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SQL)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.sql", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.sql" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:GRAPHQL|GQL))\\b\\1))", + "comment": "Heredoc with embedded GraphQL", + "end": "(?!\\G)", + "name": "meta.embedded.block.graphql", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:GRAPHQL|GQL))\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.graphql", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.graphql" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CSS)\\b\\1))", + "comment": "Heredoc with embedded CSS", + "end": "(?!\\G)", + "name": "meta.embedded.block.css", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CSS)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.css", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.css" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CPP)\\b\\1))", + "comment": "Heredoc with embedded C++", + "end": "(?!\\G)", + "name": "meta.embedded.block.cpp", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CPP)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.cpp", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.cpp" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)C)\\b\\1))", + "comment": "Heredoc with embedded C", + "end": "(?!\\G)", + "name": "meta.embedded.block.c", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)C)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.c", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.c" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1))", + "comment": "Heredoc with embedded Javascript", + "end": "(?!\\G)", + "name": "meta.embedded.block.js", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.js", + "end": "^\\s*\\2$\\n?", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.definition.string.end.ruby" } }, - "name": "meta.array.symbol.ruby", + "name": "string.unquoted.heredoc.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] + "include": "#heredoc" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] + "include": "#interpolated_ruby" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] + "include": "source.js" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] - }, + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)JQUERY)\\b\\1))", + "comment": "Heredoc with embedded jQuery Javascript", + "end": "(?!\\G)", + "name": "meta.embedded.block.js.jquery", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)JQUERY)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.js.jquery", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ { - "include": "#symbol" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\<|\\\\>", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "<", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\{|\\\\\\}", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "\\{", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] - } - ] + "include": "#heredoc" }, - "brackets": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\[|\\\\\\]", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "\\[", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "#interpolated_ruby" }, - "parens": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\(|\\\\\\)", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "\\(", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "source.js.jquery" }, - "symbol": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\\\|\\\\[ ]", - "name": "constant.other.symbol.ruby" - }, - { - "match": "\\S\\w*", - "name": "constant.other.symbol.ruby" - } - ] + { + "include": "#escaped_char" } - } - }, + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1))", + "comment": "Heredoc with embedded Shell", + "end": "(?!\\G)", + "name": "meta.embedded.block.shell", + "patterns": [ { - "begin": "%I(?:([(\\[{<])|([^\\w\\s]|_))", + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1)", "beginCaptures": { "0": { - "name": "punctuation.section.array.begin.ruby" + "name": "punctuation.definition.string.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "contentName": "source.shell", + "end": "^\\s*\\2$\\n?", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.definition.string.end.ruby" } }, - "name": "meta.array.symbol.interpolated.ruby", + "name": "string.unquoted.heredoc.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] + "include": "#heredoc" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] + "include": "#interpolated_ruby" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] + "include": "source.shell" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)LUA)\\b\\1))", + "comment": "Heredoc with embedded Lua", + "end": "(?!\\G)", + "name": "meta.embedded.block.lua", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)LUA)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.lua", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, { - "include": "#symbol" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "begin": "<", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] - } - ] + "include": "#interpolated_ruby" }, - "braces": { - "patterns": [ - { - "begin": "\\{", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "source.lua" }, - "brackets": { - "patterns": [ - { - "begin": "\\[", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)RUBY)\\b\\1))", + "comment": "Heredoc with embedded Ruby", + "end": "(?!\\G)", + "name": "meta.embedded.block.ruby", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)RUBY)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.ruby", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, - "parens": { - "patterns": [ - { - "begin": "\\(", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "#interpolated_ruby" }, - "symbol": { - "patterns": [ - { - "begin": "(?=\\\\|#\\{)", - "end": "(?!\\G)", - "name": "constant.other.symbol.ruby", - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ] - }, - { - "match": "\\S\\w*", - "name": "constant.other.symbol.ruby" - } - ] + { + "include": "source.ruby" + }, + { + "include": "#escaped_char" } - } - }, + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:YAML|YML))\\b\\1))", + "comment": "Heredoc with embedded YAML", + "end": "(?!\\G)", + "name": "meta.embedded.block.yaml", + "patterns": [ { - "begin": "%q(?:([(\\[{<])|([^\\w\\s]|_))", + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:YAML|YML))\\b\\1)", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "contentName": "source.yaml", + "end": "^\\s*\\2$\\n?", "endCaptures": { "0": { "name": "punctuation.definition.string.end.ruby" } }, - "name": "string.quoted.other.ruby", + "name": "string.unquoted.heredoc.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] + "include": "#heredoc" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] + "include": "#interpolated_ruby" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] + "include": "source.yaml" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - } - ], - "repository": { - "angles": { - "patterns": [ - { - "match": "\\\\<|\\\\>|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SLIM)\\b\\1))", + "comment": "Heredoc with embedded Slim", + "end": "(?!\\G)", + "name": "meta.embedded.block.slim", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SLIM)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.slim", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, - "braces": { - "patterns": [ - { - "match": "\\\\\\{|\\\\\\}|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] + { + "include": "#interpolated_ruby" }, - "brackets": { - "patterns": [ - { - "match": "\\\\\\[|\\\\\\]|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] + { + "include": "text.slim" }, - "parens": { - "patterns": [ - { - "match": "\\\\\\(|\\\\\\)|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?>=\\s*<<([\"'`]?)(\\w+)\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "^\\2$", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "(?>((<<[-~]([\"'`]?)(\\w+)\\3,\\s?)*<<[-~]([\"'`]?)(\\w+)\\5))(.*)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.string.begin.ruby" + }, + "7": { + "patterns": [ + { + "include": "source.ruby" } - } + ] + } + }, + "comment": "heredoc with multiple inputs and indented terminator", + "end": "^\\s*\\6$", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, { - "begin": "%Q?(?:([(\\[{<])|([^\\w\\s=]|_))", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "end": "[)\\]}>]\\2|\\1\\2", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.quoted.other.interpolated.ruby", + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "(?<={|{\\s|[^A-Za-z0-9_:@$]do|^do|[^A-Za-z0-9_:@$]do\\s|^do\\s)(\\|)", + "name": "meta.block.parameters.ruby", + "captures": { + "1": { + "name": "punctuation.separator.variable.ruby" + } + }, + "end": "(?)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - }, - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "include": "#escaped_char" + "match": "\\G([&*]?)([a-zA-Z][\\w_]*)|(_[\\w_]*)", + "captures": { + "1": { + "name": "storage.type.variable.ruby" }, - { - "include": "#interpolated_ruby" + "2": { + "name": "variable.other.block.ruby" }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] + "3": { + "name": "variable.other.block.unused.ruby variable.other.constant.ruby" } - ] + } + } + ] + }, + { + "match": ",", + "name": "punctuation.separator.variable.ruby" + } + ] + }, + { + "match": "=>", + "name": "punctuation.separator.key-value" + }, + { + "match": "->", + "name": "support.function.kernel.ruby" + }, + { + "match": "<<=|%=|&{1,2}=|\\*=|\\*\\*=|\\+=|-=|\\^=|\\|{1,2}=|<<", + "name": "keyword.operator.assignment.augmented.ruby" + }, + { + "match": "<=>|<(?!<|=)|>(?!<|=|>)|<=|>=|===|==|=~|!=|!~|(?<=[ \\t])\\?", + "name": "keyword.operator.comparison.ruby" + }, + { + "match": "(?>", + "name": "keyword.operator.other.ruby" + }, + { + "match": ";", + "name": "punctuation.separator.statement.ruby" + }, + { + "match": ",", + "name": "punctuation.separator.object.ruby" + }, + { + "comment": "Mark as namespace separator if double colons followed by capital letter", + "match": "(::)\\s*(?=[A-Z])", + "captures": { + "1": { + "name": "punctuation.separator.namespace.ruby" + } + } + }, + { + "comment": "Mark as method separator if double colons not followed by capital letter", + "match": "(\\.|::)\\s*(?![A-Z])", + "captures": { + "1": { + "name": "punctuation.separator.method.ruby" + } + } + }, + { + "comment": "Must come after method and constant separators to prefer double colons", + "match": ":", + "name": "punctuation.separator.other.ruby" + }, + { + "match": "{", + "name": "punctuation.section.scope.begin.ruby" + }, + { + "match": "}", + "name": "punctuation.section.scope.end.ruby" + }, + { + "match": "\\[", + "name": "punctuation.section.array.begin.ruby" + }, + { + "match": "]", + "name": "punctuation.section.array.end.ruby" + }, + { + "match": "\\(|\\)", + "name": "punctuation.section.function.ruby" + }, + { + "name": "meta.function-call.ruby", + "begin": "(?<=[^\\.]\\.|::)(?=[a-zA-Z][a-zA-Z0-9_!?]*[^a-zA-Z0-9_!?])", + "end": "(?<=[a-zA-Z0-9_!?])(?=[^a-zA-Z0-9_!?])", + "patterns": [ + { + "name": "entity.name.function.ruby", + "match": "([a-zA-Z][a-zA-Z0-9_!?]*)(?=[^a-zA-Z0-9_!?])" + } + ] + }, + { + "begin": "([a-zA-Z]\\w*[!?]?)(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.ruby" + }, + "2": { + "name": "punctuation.section.function.ruby" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.function.ruby" + } + }, + "name": "meta.function-call.ruby", + "patterns": [ + { + "include": "$self" + } + ] + } + ], + "repository": { + "method_parameters": { + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#braces" + }, + { + "include": "#brackets" + }, + { + "include": "#params" + }, + { + "include": "$self" + } + ], + "repository": { + "params": { + "captures": { + "1": { + "name": "storage.type.variable.ruby" }, - "braces": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] + "2": { + "name": "constant.other.symbol.hashkey.parameter.function.ruby" }, - "brackets": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] + "3": { + "name": "punctuation.definition.constant.ruby" }, - "parens": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + "4": { + "name": "variable.parameter.function.ruby" } - } + }, + "match": "\\G(&|\\*\\*?)?(?:([_a-zA-Z]\\w*[?!]?(:))|([_a-zA-Z]\\w*))" }, - { - "begin": "%r(?:([(\\[{<])|([^\\w\\s]|_))", + "braces": { + "begin": "\\{", "beginCaptures": { "0": { - "name": "punctuation.definition.string.begin.ruby" + "name": "punctuation.section.scope.begin.ruby" } }, - "end": "([)\\]}>]\\2|\\1\\2)[eimnosux]*", + "end": "\\}", "endCaptures": { "0": { - "name": "punctuation.definition.string.end.ruby" + "name": "punctuation.section.scope.end.ruby" } }, - "name": "string.regexp.percent.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] + "include": "#parens" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] + "include": "#braces" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] + "include": "#brackets" }, { - "include": "#regex_sub" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + "include": "$self" } - } + ] }, - { - "begin": "%s(?:([(\\[{<])|([^\\w\\s]|_))", + "brackets": { + "begin": "\\[", "beginCaptures": { "0": { - "name": "punctuation.definition.constant.begin.ruby" + "name": "punctuation.section.array.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "end": "\\]", "endCaptures": { "0": { - "name": "punctuation.definition.constant.end.ruby" + "name": "punctuation.section.array.end.ruby" } }, - "name": "constant.other.symbol.percent.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] + "include": "#parens" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] + "include": "#braces" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] + "include": "#brackets" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - } - ], - "repository": { - "angles": { - "patterns": [ - { - "match": "\\\\<|\\\\>|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "match": "\\\\\\{|\\\\\\}|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "match": "\\\\\\[|\\\\\\]|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "match": "\\\\\\(|\\\\\\)|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + "include": "$self" } - } + ] }, - { - "begin": "%w(?:([(\\[{<])|([^\\w\\s]|_))", + "parens": { + "begin": "\\(", "beginCaptures": { "0": { - "name": "punctuation.section.array.begin.ruby" + "name": "punctuation.section.function.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "end": "\\)", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.section.function.end.ruby" } }, - "name": "meta.array.string.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] + "include": "#parens" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] + "include": "#braces" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] + "include": "#brackets" }, { - "include": "#string" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\<|\\\\>", - "name": "string.other.ruby" - }, - { - "begin": "<", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\{|\\\\\\}", - "name": "string.other.ruby" - }, - { - "begin": "\\{", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\[|\\\\\\]", - "name": "string.other.ruby" - }, - { - "begin": "\\[", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\(|\\\\\\)", - "name": "string.other.ruby" - }, - { - "begin": "\\(", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - } - ] - }, - "string": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\\\|\\\\[ ]", - "name": "string.other.ruby" - }, - { - "match": "\\S\\w*", - "name": "string.other.ruby" - } - ] + "include": "$self" } - } - }, + ] + } + } + }, + "escaped_char": { + "match": "\\\\(?:[0-7]{1,3}|x[\\da-fA-F]{1,2}|.)", + "name": "constant.character.escape.ruby" + }, + "heredoc": { + "begin": "^<<[-~]?\\w+", + "end": "$", + "patterns": [ + { + "include": "$self" + } + ] + }, + "interpolated_ruby": { + "patterns": [ { - "begin": "%W(?:([(\\[{<])|([^\\w\\s]|_))", + "begin": "#{", "beginCaptures": { "0": { - "name": "punctuation.section.array.begin.ruby" + "name": "punctuation.section.embedded.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "contentName": "source.ruby", + "end": "}", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.section.embedded.end.ruby" } }, - "name": "meta.array.string.interpolated.ruby", + "name": "meta.embedded.line.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] - }, - { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] + "include": "#nest_curly_and_self" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] - }, + "include": "$self" + } + ] + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#@)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.instance.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#@@)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.class.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#\\$)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.global.ruby" + } + ] + }, + "nest_brackets": { + "begin": "\\[", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "]", + "patterns": [ + { + "include": "#nest_brackets" + } + ] + }, + "nest_brackets_i": { + "begin": "\\[", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "]", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_brackets_i" + } + ] + }, + "nest_brackets_r": { + "begin": "\\[", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "]", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_brackets_r" + } + ] + }, + "nest_curly": { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ + { + "include": "#nest_curly" + } + ] + }, + "nest_curly_and_self": { + "patterns": [ + { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ { - "include": "#string" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "begin": "<", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "begin": "\\{", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "begin": "\\[", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "begin": "\\(", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - } - ] - }, - "string": { - "patterns": [ - { - "begin": "(?=\\\\|#\\{)", - "end": "(?!\\G)", - "name": "string.other.ruby", - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ] - }, - { - "match": "\\S\\w*", - "name": "string.other.ruby" - } - ] + "include": "#nest_curly_and_self" } - } + ] + }, + { + "include": "$self" + } + ] + }, + "nest_curly_i": { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" + } + ] + }, + "nest_curly_r": { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_curly_r" + } + ] + }, + "nest_ltgt": { + "begin": "<", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#nest_ltgt" + } + ] + }, + "nest_ltgt_i": { + "begin": "<", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" + } + ] + }, + "nest_ltgt_r": { + "begin": "<", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_ltgt_r" + } + ] + }, + "nest_parens": { + "begin": "\\(", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#nest_parens" + } + ] + }, + "nest_parens_i": { + "begin": "\\(", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#interpolated_ruby" }, { - "begin": "%x(?:([(\\[{<])|([^\\w\\s]|_))", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "end": "[)\\]}>]\\2|\\1\\2", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.interpolated.percent.ruby", - "patterns": [ - { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] - }, - { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] - }, - { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - }, - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] - } - } + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" + } + ] + }, + "nest_parens_r": { + "begin": "\\(", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_parens_r" } ] }, @@ -2694,29 +2804,24 @@ { "captures": { "1": { - "name": "punctuation.definition.quantifier.begin.ruby" + "name": "punctuation.definition.arbitrary-repetition.ruby" }, "3": { - "name": "punctuation.definition.quantifier.end.ruby" + "name": "punctuation.definition.arbitrary-repetition.ruby" } }, - "match": "(\\{)\\d+(,\\d+)?(\\})", - "name": "keyword.operator.quantifier.ruby" + "match": "({)\\d+(,\\d+)?(})", + "name": "string.regexp.arbitrary-repetition.ruby" }, { - "begin": "\\[\\^?", - "beginCaptures": { - "0": { - "name": "punctuation.definition.character-class.begin.ruby" - } - }, - "end": "\\]", - "endCaptures": { + "begin": "\\[(?:\\^?])?", + "captures": { "0": { - "name": "punctuation.definition.character-class.end.ruby" + "name": "punctuation.definition.character-class.ruby" } }, - "name": "constant.other.character-class.set.ruby", + "end": "]", + "name": "string.regexp.character-class.ruby", "patterns": [ { "include": "#escaped_char" @@ -2751,7 +2856,7 @@ } }, "end": "\\)", - "name": "meta.group.regexp.ruby", + "name": "string.regexp.group.ruby", "patterns": [ { "include": "#regex_sub" @@ -2767,9 +2872,328 @@ }, "comment": "We are restrictive in what we allow to go after the comment character to avoid false positives, since the availability of comments depend on regexp flags.", "end": "$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, "name": "comment.line.number-sign.ruby" } ] + }, + "yard": { + "patterns": [ + { + "include": "#yard_comment" + }, + { + "include": "#yard_param_types" + }, + { + "include": "#yard_option" + }, + { + "include": "#yard_tag" + }, + { + "include": "#yard_types" + }, + { + "include": "#yard_directive" + }, + { + "include": "#yard_see" + }, + { + "include": "#yard_macro_attribute" + } + ] + }, + "yard_see": { + "comment": "separate rule for @see because name could contain url", + "begin": "^(\\s*)(#)(\\s*)(@)(see)(?=\\s)(\\s+(.+?))?(?=\\s|$)", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.parameter.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_macro_attribute": { + "comment": "separate rule for attribute and macro tags because name goes after []", + "begin": "^(\\s*)(#)(\\s*)(@!)(attribute|macro)(\\s+((\\[).+(])))?(?=\\s)(\\s+([a-z_]\\w*:?))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + }, + "11": { + "name": "comment.line.parameter.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_comment": { + "comment": "For YARD tags that follow the tag-comment pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(abstract|api|author|deprecated|example|macro|note|overload|since|todo|version)(?=\\s|$)", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_param_types": { + "comment": "For YARD tags that follow the tag-name-types-description or tag-types-name-description pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(attr|attr_reader|attr_writer|yieldparam|param)(?=\\s)(?>\\s+(?>([a-z_]\\w*:?)|((\\[).+(]))))?(?>\\s+(?>((\\[).+(]))|([a-z_]\\w*:?)))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "6": { + "name": "comment.line.parameter.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + }, + "10": { + "name": "comment.line.type.yard.ruby" + }, + "11": { + "name": "comment.line.punctuation.yard.ruby" + }, + "12": { + "name": "comment.line.punctuation.yard.ruby" + }, + "13": { + "name": "comment.line.parameter.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_option": { + "comment": "For YARD option tag that follow the tag-name-types-key-(value)-description pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(option)(?=\\s)(?>\\s+([a-z_]\\w*:?))?(?>\\s+((\\[).+(])))?(?>\\s+((\\S*)))?(?>\\s+((\\().+(\\))))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "6": { + "name": "comment.line.parameter.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + }, + "10": { + "name": "comment.line.keyword.yard.ruby" + }, + "11": { + "name": "comment.line.hashkey.yard.ruby" + }, + "12": { + "name": "comment.line.defaultvalue.yard.ruby" + }, + "13": { + "name": "comment.line.punctuation.yard.ruby" + }, + "14": { + "name": "comment.line.punctuation.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_tag": { + "comment": "For YARD tags that are just the tag", + "match": "^(\\s*)(#)(\\s*)(@)(private)$", + "captures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + } + }, + "name": "comment.line.number-sign.ruby" + }, + "yard_types": { + "comment": "For YARD tags that follow the tag-types-comment pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(raise|return|yield(?:return)?)(?=\\s)(\\s+((\\[).+(])))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_directive": { + "comment": "For YARD directives", + "begin": "^(\\s*)(#)(\\s*)(@!)(endgroup|group|method|parse|scope|visibility)(\\s+((\\[).+(])))?(?=\\s)", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_continuation": { + "match": "^\\s*#", + "name": "punctuation.definition.comment.ruby" } } } \ No newline at end of file diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index 5565e2dbec6..8328860a9ff 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -64,7 +64,8 @@ "support.constant.math", "support.constant.dom", "support.constant.json", - "entity.other.inherited-class" + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" ], "settings": { "foreground": "#4EC9B0" diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index ebb49d98644..b3a1a1f065f 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -395,7 +395,8 @@ "support.constant.math", "support.constant.dom", "support.constant.json", - "entity.other.inherited-class" + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" ], "settings": { "foreground": "#4EC9B0" diff --git a/extensions/theme-defaults/themes/hc_light.json b/extensions/theme-defaults/themes/hc_light.json index 460f26ad738..6d192e76757 100644 --- a/extensions/theme-defaults/themes/hc_light.json +++ b/extensions/theme-defaults/themes/hc_light.json @@ -429,7 +429,8 @@ "support.constant.math", "support.constant.dom", "support.constant.json", - "entity.other.inherited-class" + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" ], "settings": { "foreground": "#185E73" diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index 7f77883f3ee..b1b4e45c2fe 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -64,7 +64,8 @@ "support.constant.math", "support.constant.dom", "support.constant.json", - "entity.other.inherited-class" + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" ], "settings": { "foreground": "#267f99" diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index ec50a96586a..d526a33e9a8 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -187,7 +187,8 @@ "scope": [ "string", "constant.other.symbol", - "entity.other.inherited-class" + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" ], "settings": { "foreground": "#889b4a" diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index 3f41fb052e0..a79ad385326 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -160,7 +160,10 @@ }, { "name": "Inherited class", - "scope": "entity.other.inherited-class", + "scope": [ + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" + ], "settings": { "fontStyle": "", "foreground": "#C7444A" diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 9a510748714..829b33b9355 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -213,7 +213,10 @@ }, { "name": "Inherited class", - "scope": "entity.other.inherited-class", + "scope": [ + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" + ], "settings": { "fontStyle": "italic underline", "foreground": "#A6E22E" diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 0b6cd0be740..47d78a679cb 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -127,6 +127,7 @@ "entity.name.namespace", "entity.name.scope-resolution", "entity.other.inherited-class", + "punctuation.separator.namespace.ruby", "support.class" ], "settings": { diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 233fd9e83da..4625df1a5d5 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -150,8 +150,11 @@ }, { "name": "Entity inherited-class", - "scope": "entity.other.inherited-class", - "settings": { + "scope": [ + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" + ], + "settings": { "fontStyle": "underline", "foreground": "#aa5507ff" } diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index e67135a9d99..af1e7a913ff 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -140,7 +140,10 @@ }, { "name": "Inherited class", - "scope": "entity.other.inherited-class", + "scope": [ + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby" + ], "settings": { "foreground": "#6C71C4" } diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index d5f6dc11bf0..11a71e6635b 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -140,7 +140,10 @@ }, { "name": "Inherited class", - "scope": "entity.other.inherited-class", + "scope": [ + "entity.other.inherited-class", + "punctuation.separator.namespace.ruby", + ], "settings": { "foreground": "#6C71C4" } diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json index 89bc8acd36f..3d313ef2e1e 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json @@ -117,7 +117,7 @@ }, { "name": "String, Symbols, Inherited Class, Markup Heading, GitGutter inserted", - "scope": "string, constant.other.symbol, entity.other.inherited-class, markup.heading, markup.inserted.git_gutter", + "scope": "string, constant.other.symbol, entity.other.inherited-class, punctuation.separator.namespace.ruby, markup.heading, markup.inserted.git_gutter", "settings": { "fontStyle": "", "foreground": "#D1F1A9" diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_rb.json b/extensions/vscode-colorize-tests/test/colorize-results/test_rb.json index d36726f9866..6230a5f14b3 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_rb.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_rb.json @@ -113,7 +113,7 @@ }, { "c": "module", - "t": "source.ruby meta.module.ruby keyword.control.module.ruby", + "t": "source.ruby keyword.control.ruby", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -127,7 +127,7 @@ }, { "c": " ", - "t": "source.ruby meta.module.ruby", + "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -141,72 +141,72 @@ }, { "c": "Azure", - "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.first.ruby", + "t": "source.ruby support.class.ruby", "r": { - "dark_plus": "entity.other.inherited-class: #4EC9B0", - "light_plus": "entity.other.inherited-class: #267F99", + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.other.inherited-class: #4EC9B0", - "dark_modern": "entity.other.inherited-class: #4EC9B0", - "hc_light": "entity.other.inherited-class: #185E73", - "light_modern": "entity.other.inherited-class: #267F99" + "hc_black": "support.class: #4EC9B0", + "dark_modern": "support.class: #4EC9B0", + "hc_light": "support.class: #185E73", + "light_modern": "support.class: #267F99" } }, { "c": "::", - "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.first.ruby punctuation.separator.inheritance.ruby", + "t": "source.ruby punctuation.separator.namespace.ruby", "r": { - "dark_plus": "entity.other.inherited-class: #4EC9B0", - "light_plus": "entity.other.inherited-class: #267F99", + "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", + "light_plus": "punctuation.separator.namespace.ruby: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.other.inherited-class: #4EC9B0", - "dark_modern": "entity.other.inherited-class: #4EC9B0", - "hc_light": "entity.other.inherited-class: #185E73", - "light_modern": "entity.other.inherited-class: #267F99" + "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", + "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", + "hc_light": "punctuation.separator.namespace.ruby: #185E73", + "light_modern": "punctuation.separator.namespace.ruby: #267F99" } }, { "c": "ARM", - "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.second.ruby", + "t": "source.ruby support.class.ruby", "r": { - "dark_plus": "entity.other.inherited-class: #4EC9B0", - "light_plus": "entity.other.inherited-class: #267F99", + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.other.inherited-class: #4EC9B0", - "dark_modern": "entity.other.inherited-class: #4EC9B0", - "hc_light": "entity.other.inherited-class: #185E73", - "light_modern": "entity.other.inherited-class: #267F99" + "hc_black": "support.class: #4EC9B0", + "dark_modern": "support.class: #4EC9B0", + "hc_light": "support.class: #185E73", + "light_modern": "support.class: #267F99" } }, { "c": "::", - "t": "source.ruby meta.module.ruby entity.name.type.module.ruby entity.other.inherited-class.module.second.ruby punctuation.separator.inheritance.ruby", + "t": "source.ruby punctuation.separator.namespace.ruby", "r": { - "dark_plus": "entity.other.inherited-class: #4EC9B0", - "light_plus": "entity.other.inherited-class: #267F99", + "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", + "light_plus": "punctuation.separator.namespace.ruby: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.other.inherited-class: #4EC9B0", - "dark_modern": "entity.other.inherited-class: #4EC9B0", - "hc_light": "entity.other.inherited-class: #185E73", - "light_modern": "entity.other.inherited-class: #267F99" + "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", + "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", + "hc_light": "punctuation.separator.namespace.ruby: #185E73", + "light_modern": "punctuation.separator.namespace.ruby: #267F99" } }, { "c": "Scheduler", - "t": "source.ruby meta.module.ruby entity.name.type.module.ruby", + "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0", - "dark_modern": "entity.name.type: #4EC9B0", - "hc_light": "entity.name.type: #185E73", - "light_modern": "entity.name.type: #267F99" + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" } }, { @@ -309,7 +309,7 @@ }, { "c": " ", - "t": "source.ruby meta.class.ruby", + "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -323,7 +323,7 @@ }, { "c": "class", - "t": "source.ruby meta.class.ruby keyword.control.class.ruby", + "t": "source.ruby keyword.control.ruby", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -337,7 +337,7 @@ }, { "c": " ", - "t": "source.ruby meta.class.ruby", + "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -351,21 +351,21 @@ }, { "c": "SchedulerManagementClient", - "t": "source.ruby meta.class.ruby entity.name.type.class.ruby", + "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0", - "dark_modern": "entity.name.type: #4EC9B0", - "hc_light": "entity.name.type: #185E73", - "light_modern": "entity.name.type: #267F99" + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" } }, { "c": " ", - "t": "source.ruby meta.class.ruby", + "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -379,7 +379,7 @@ }, { "c": "<", - "t": "source.ruby meta.class.ruby keyword.operator.other.ruby", + "t": "source.ruby keyword.operator.comparison.ruby", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -393,7 +393,7 @@ }, { "c": " ", - "t": "source.ruby meta.class.ruby", + "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -406,17 +406,45 @@ } }, { - "c": "MsRestAzure::AzureServiceClient", - "t": "source.ruby meta.class.ruby entity.other.inherited-class.ruby", + "c": "MsRestAzure", + "t": "source.ruby support.class.ruby", + "r": { + "dark_plus": "support.class: #4EC9B0", + "light_plus": "support.class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.class: #4EC9B0", + "dark_modern": "support.class: #4EC9B0", + "hc_light": "support.class: #185E73", + "light_modern": "support.class: #267F99" + } + }, + { + "c": "::", + "t": "source.ruby punctuation.separator.namespace.ruby", + "r": { + "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", + "light_plus": "punctuation.separator.namespace.ruby: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", + "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", + "hc_light": "punctuation.separator.namespace.ruby: #185E73", + "light_modern": "punctuation.separator.namespace.ruby: #267F99" + } + }, + { + "c": "AzureServiceClient", + "t": "source.ruby variable.other.constant.ruby", "r": { - "dark_plus": "entity.other.inherited-class: #4EC9B0", - "light_plus": "entity.other.inherited-class: #267F99", + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.other.inherited-class: #4EC9B0", - "dark_modern": "entity.other.inherited-class: #4EC9B0", - "hc_light": "entity.other.inherited-class: #185E73", - "light_modern": "entity.other.inherited-class: #267F99" + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" } }, { @@ -479,14 +507,14 @@ "c": "::", "t": "source.ruby punctuation.separator.namespace.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", + "light_plus": "punctuation.separator.namespace.ruby: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", + "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", + "hc_light": "punctuation.separator.namespace.ruby: #185E73", + "light_modern": "punctuation.separator.namespace.ruby: #267F99" } }, { @@ -507,14 +535,14 @@ "c": "::", "t": "source.ruby punctuation.separator.namespace.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", + "light_plus": "punctuation.separator.namespace.ruby: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", + "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", + "hc_light": "punctuation.separator.namespace.ruby: #185E73", + "light_modern": "punctuation.separator.namespace.ruby: #267F99" } }, { @@ -535,14 +563,14 @@ "c": "::", "t": "source.ruby punctuation.separator.namespace.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", + "light_plus": "punctuation.separator.namespace.ruby: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", + "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", + "hc_light": "punctuation.separator.namespace.ruby: #185E73", + "light_modern": "punctuation.separator.namespace.ruby: #267F99" } }, { @@ -617,16 +645,16 @@ }, { "c": " ", - "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" } }, { @@ -644,7 +672,7 @@ } }, { - "c": " @return job_collections", + "c": " ", "t": "source.ruby comment.line.number-sign.ruby", "r": { "dark_plus": "comment: #6A9955", @@ -657,6 +685,48 @@ "light_modern": "comment: #008000" } }, + { + "c": "@", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.punctuation.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "return", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " job_collections", + "t": "source.ruby comment.line.number-sign.ruby comment.line.string.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, { "c": " ", "t": "source.ruby", @@ -701,30 +771,30 @@ }, { "c": ":", - "t": "source.ruby constant.other.symbol.ruby punctuation.definition.constant.ruby", + "t": "source.ruby constant.language.symbol.ruby punctuation.definition.constant.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" } }, { "c": "job_collections", - "t": "source.ruby constant.other.symbol.ruby", + "t": "source.ruby constant.language.symbol.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" } }, { @@ -784,7 +854,385 @@ } }, { - "c": " Creates initializes a new instance of the SchedulerManagementClient class.", + "c": " Creates initializes a new instance of the SchedulerManagementClient class.", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "@", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.punctuation.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "param", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "credentials", + "t": "source.ruby comment.line.number-sign.ruby comment.line.parameter.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "[", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby comment.line.punctuation.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "MsRest::ServiceClientCredentials", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "]", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby comment.line.punctuation.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " credentials to authorize HTTP requests made by the service client.", + "t": "source.ruby comment.line.number-sign.ruby comment.line.string.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "@", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.punctuation.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "param", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "base_url", + "t": "source.ruby comment.line.number-sign.ruby comment.line.parameter.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "[", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby comment.line.punctuation.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "String", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "]", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby comment.line.punctuation.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " the base URI of the service.", + "t": "source.ruby comment.line.number-sign.ruby comment.line.string.yard.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", + "t": "source.ruby comment.line.number-sign.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": "#", + "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" + } + }, + { + "c": " ", "t": "source.ruby comment.line.number-sign.ruby", "r": { "dark_plus": "comment: #6A9955", @@ -798,22 +1246,22 @@ } }, { - "c": " ", - "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "c": "@", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.punctuation.yard.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" } }, { - "c": "#", - "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "c": "param", + "t": "source.ruby comment.line.number-sign.ruby comment.line.keyword.yard.ruby", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -826,7 +1274,7 @@ } }, { - "c": " @param credentials [MsRest::ServiceClientCredentials] credentials to authorize HTTP requests made by the service client.", + "c": " ", "t": "source.ruby comment.line.number-sign.ruby", "r": { "dark_plus": "comment: #6A9955", @@ -840,22 +1288,8 @@ } }, { - "c": " ", - "t": "source.ruby punctuation.whitespace.comment.leading.ruby", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" - } - }, - { - "c": "#", - "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "c": "options", + "t": "source.ruby comment.line.number-sign.ruby comment.line.parameter.yard.ruby", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -868,7 +1302,7 @@ } }, { - "c": " @param base_url [String] the base URI of the service.", + "c": " ", "t": "source.ruby comment.line.number-sign.ruby", "r": { "dark_plus": "comment: #6A9955", @@ -882,22 +1316,22 @@ } }, { - "c": " ", - "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "c": "[", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby comment.line.punctuation.yard.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" } }, { - "c": "#", - "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "c": "Array", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -910,8 +1344,8 @@ } }, { - "c": " @param options [Array] filters to be applied to the HTTP requests.", - "t": "source.ruby comment.line.number-sign.ruby", + "c": "]", + "t": "source.ruby comment.line.number-sign.ruby comment.line.type.yard.ruby comment.line.punctuation.yard.ruby", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -924,22 +1358,22 @@ } }, { - "c": " ", - "t": "source.ruby punctuation.whitespace.comment.leading.ruby", + "c": " filters to be applied to the HTTP requests.", + "t": "source.ruby comment.line.number-sign.ruby comment.line.string.yard.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" } }, { - "c": "#", - "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", + "c": " #", + "t": "source.ruby comment.line.number-sign.ruby comment.line.string.yard.ruby punctuation.definition.comment.ruby", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1036,7 +1470,21 @@ } }, { - "c": ", ", + "c": ",", + "t": "source.ruby meta.function.method.with-arguments.ruby punctuation.separator.object.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", "t": "source.ruby meta.function.method.with-arguments.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -1107,7 +1555,7 @@ }, { "c": "nil", - "t": "source.ruby meta.function.method.with-arguments.ruby constant.language.ruby", + "t": "source.ruby meta.function.method.with-arguments.ruby constant.language.nil.ruby", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1120,7 +1568,21 @@ } }, { - "c": ", ", + "c": ",", + "t": "source.ruby meta.function.method.with-arguments.ruby punctuation.separator.object.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", "t": "source.ruby meta.function.method.with-arguments.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -1191,7 +1653,7 @@ }, { "c": "nil", - "t": "source.ruby meta.function.method.with-arguments.ruby constant.language.ruby", + "t": "source.ruby meta.function.method.with-arguments.ruby constant.language.nil.ruby", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1653,16 +2115,16 @@ }, { "c": "nil?", - "t": "source.ruby", + "t": "source.ruby meta.function-call.ruby entity.name.function.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" } }, { @@ -1849,16 +2311,16 @@ }, { "c": "is_a?", - "t": "source.ruby", + "t": "source.ruby meta.function-call.ruby entity.name.function.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" } }, { @@ -1893,14 +2355,14 @@ "c": "::", "t": "source.ruby punctuation.separator.namespace.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", + "light_plus": "punctuation.separator.namespace.ruby: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", + "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", + "hc_light": "punctuation.separator.namespace.ruby: #185E73", + "light_modern": "punctuation.separator.namespace.ruby: #267F99" } }, { @@ -2563,7 +3025,7 @@ }, { "c": "30", - "t": "source.ruby constant.numeric.integer.ruby", + "t": "source.ruby constant.numeric.ruby", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2661,7 +3123,7 @@ }, { "c": "true", - "t": "source.ruby constant.language.ruby", + "t": "source.ruby constant.language.boolean.ruby", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -2744,7 +3206,21 @@ } }, { - "c": "version ", + "c": "version", + "t": "source.ruby meta.function-call.ruby entity.name.function.ruby", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": " ", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -2787,34 +3263,34 @@ }, { "c": ":", - "t": "source.ruby constant.other.symbol.ruby punctuation.definition.constant.ruby", + "t": "source.ruby constant.language.symbol.ruby punctuation.definition.constant.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" } }, { "c": "mavericks", - "t": "source.ruby constant.other.symbol.ruby", + "t": "source.ruby constant.language.symbol.ruby", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" } }, { - "c": " version ", + "c": " ", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -2828,21 +3304,21 @@ } }, { - "c": "=", - "t": "source.ruby keyword.operator.assignment.ruby", + "c": "version", + "t": "source.ruby variable.ruby", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4", - "dark_modern": "keyword.operator: #D4D4D4", - "hc_light": "keyword.operator: #000000", - "light_modern": "keyword.operator: #000000" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" } }, { - "c": " ", + "c": " = ", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -2899,7 +3375,7 @@ }, { "c": "}", - "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby punctuation.section.embedded.end.ruby source.ruby", + "t": "source.ruby string.interpolated.ruby meta.embedded.line.ruby punctuation.section.embedded.end.ruby", "r": { "dark_plus": "punctuation.section.embedded: #569CD6", "light_plus": "punctuation.section.embedded: #0000FF", @@ -2968,7 +3444,7 @@ } }, { - "c": " version ", + "c": " ", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -2982,21 +3458,21 @@ } }, { - "c": "=", - "t": "source.ruby keyword.operator.assignment.ruby", + "c": "version", + "t": "source.ruby variable.ruby", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4", - "dark_modern": "keyword.operator: #D4D4D4", - "hc_light": "keyword.operator: #000000", - "light_modern": "keyword.operator: #000000" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" } }, { - "c": " ", + "c": " = ", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -3080,7 +3556,7 @@ } }, { - "c": " version ", + "c": " ", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -3094,21 +3570,21 @@ } }, { - "c": "=", - "t": "source.ruby keyword.operator.assignment.ruby", + "c": "version", + "t": "source.ruby variable.ruby", "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4", - "dark_modern": "keyword.operator: #D4D4D4", - "hc_light": "keyword.operator: #000000", - "light_modern": "keyword.operator: #000000" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" } }, { - "c": " version", + "c": " = version", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -3137,7 +3613,7 @@ }, { "c": "/", - "t": "source.ruby string.regexp.classic.ruby punctuation.definition.string.ruby", + "t": "source.ruby string.regexp.interpolated.ruby punctuation.section.regexp.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3151,7 +3627,7 @@ }, { "c": "clang-", - "t": "source.ruby string.regexp.classic.ruby", + "t": "source.ruby string.regexp.interpolated.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3165,7 +3641,7 @@ }, { "c": "(", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby punctuation.definition.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3179,7 +3655,7 @@ }, { "c": "\\d", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", @@ -3193,7 +3669,7 @@ }, { "c": "+", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3207,7 +3683,7 @@ }, { "c": "\\.\\d", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", @@ -3221,7 +3697,7 @@ }, { "c": "+", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3235,7 +3711,7 @@ }, { "c": "\\.\\d", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", @@ -3249,7 +3725,7 @@ }, { "c": "+", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3263,7 +3739,7 @@ }, { "c": "(", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby string.regexp.group.ruby punctuation.definition.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3277,7 +3753,7 @@ }, { "c": "\\.\\d", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby constant.character.escape.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby string.regexp.group.ruby constant.character.escape.ruby", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", @@ -3291,7 +3767,7 @@ }, { "c": "+", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby string.regexp.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3305,7 +3781,7 @@ }, { "c": ")", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby string.regexp.group.ruby punctuation.definition.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3319,7 +3795,7 @@ }, { "c": "?", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3333,7 +3809,7 @@ }, { "c": ")", - "t": "source.ruby string.regexp.classic.ruby meta.group.regexp.ruby punctuation.definition.group.ruby", + "t": "source.ruby string.regexp.interpolated.ruby string.regexp.group.ruby punctuation.definition.group.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3347,7 +3823,7 @@ }, { "c": "/", - "t": "source.ruby string.regexp.classic.ruby punctuation.definition.string.ruby", + "t": "source.ruby string.regexp.interpolated.ruby punctuation.section.regexp.ruby", "r": { "dark_plus": "string.regexp: #D16969", "light_plus": "string.regexp: #811F3F", @@ -3389,7 +3865,7 @@ }, { "c": "1", - "t": "source.ruby constant.numeric.integer.ruby", + "t": "source.ruby constant.numeric.ruby", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -3459,7 +3935,7 @@ }, { "c": "\"", - "t": "source.ruby string.quoted.double.ruby punctuation.definition.string.begin.ruby", + "t": "source.ruby string.quoted.double.interpolated.ruby punctuation.definition.string.begin.ruby", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -3473,7 +3949,7 @@ }, { "c": "0", - "t": "source.ruby string.quoted.double.ruby", + "t": "source.ruby string.quoted.double.interpolated.ruby", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -3487,7 +3963,7 @@ }, { "c": "\"", - "t": "source.ruby string.quoted.double.ruby punctuation.definition.string.end.ruby", + "t": "source.ruby string.quoted.double.interpolated.ruby punctuation.definition.string.end.ruby", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", From 4e8fbaef741afebd24684b88cac47c2f44dfb8eb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 11 Dec 2024 17:58:52 +0100 Subject: [PATCH 111/479] update doc comment (#235835) fixes https://github.com/microsoft/vscode/issues/209888 --- src/vs/editor/common/languages.ts | 3 --- src/vs/monaco.d.ts | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 889a89a5d68..9d97fb451a8 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -556,9 +556,6 @@ export interface CompletionItem { /** * A range of text that should be replaced by this completion item. * - * Defaults to a range from the start of the {@link TextDocument.getWordRangeAtPosition current word} to the - * current position. - * * *Note:* The range must be a {@link Range.isSingleLine single line} and it must * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. */ diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 7dddce76da2..eba21bfb561 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7103,9 +7103,6 @@ declare namespace monaco.languages { /** * A range of text that should be replaced by this completion item. * - * Defaults to a range from the start of the {@link TextDocument.getWordRangeAtPosition current word} to the - * current position. - * * *Note:* The range must be a {@link Range.isSingleLine single line} and it must * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. */ From 22a35bc9c40c476e63eaf95f5494476920185f1b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 11 Dec 2024 18:20:10 +0100 Subject: [PATCH 112/479] [json] jsonValidation isnt working in multi extension host setup (#235845) --- extensions/json-language-features/client/src/jsonClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 90aafc89b84..cc5b27339ac 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -597,7 +597,7 @@ async function startClientWithParticipants(context: ExtensionContext, languagePa function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] { const associations: ISchemaAssociation[] = []; - extensions.all.forEach(extension => { + extensions.allAcrossExtensionHosts.forEach(extension => { const packageJSON = extension.packageJSON; if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) { const jsonValidation = packageJSON.contributes.jsonValidation; From 7a2c521c77a4e8f809ad0793f160aaceb5c10a09 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 11 Dec 2024 18:31:44 +0100 Subject: [PATCH 113/479] Also reveal horizontally (#235841) --- .../inlineCompletions/browser/model/inlineCompletionsModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index ed98294be69..f40e12bdf88 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -742,7 +742,7 @@ export class InlineCompletionsModel extends Disposable { this._jumpedTo.set(true, tx); this.dontRefetchSignal.trigger(tx); this._editor.setPosition(s.inlineEdit.range.getStartPosition(), 'inlineCompletions.jump'); - this._editor.revealLine(s.inlineEdit.range.startLineNumber); + this._editor.revealPosition(s.inlineEdit.range.getStartPosition()); this._editor.focus(); }); } From 3c1a4ddfd8dab46425f67c6af037eb5037628a00 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 11 Dec 2024 18:43:36 +0100 Subject: [PATCH 114/479] Remove default background colors and allow to style the borders independently (#235847) --- .../view/inlineEdits/inlineEditsView.ts | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) 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 3ea99c7e16a..8b081fe0c11 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts @@ -22,7 +22,7 @@ import './inlineEditsView.css'; import { IOriginalEditorInlineDiffViewState, OriginalEditorInlineDiffView } from './inlineDiffView.js'; import { applyEditToModifiedRangeMappings, createReindentEdit, getOffsetForPos, maxContentWidthInRange, PathBuilder, Point, StatusBarViewItem } from './utils.js'; import { IInlineEditsIndicatorState, InlineEditsIndicator } from './inlineEditsIndicatorView.js'; -import { darken, lighten, registerColor, transparent } from '../../../../../../platform/theme/common/colorUtils.js'; +import { darken, lighten, registerColor } from '../../../../../../platform/theme/common/colorUtils.js'; import { diffInserted, diffRemoved } from '../../../../../../platform/theme/common/colorRegistry.js'; import { CustomizedMenuWorkbenchToolBar } from '../../hintsWidget/inlineCompletionsHintsWidget.js'; import { Command } from '../../../../../common/languages.js'; @@ -38,13 +38,13 @@ import { Color } from '../../../../../../base/common/color.js'; export const originalBackgroundColor = registerColor( 'inlineEdit.originalBackground', - transparent(diffRemoved, 0.4), + Color.transparent, '', true ); export const modifiedBackgroundColor = registerColor( 'inlineEdit.modifiedBackground', - transparent(diffInserted, 0.4), + Color.transparent, '', true ); @@ -77,8 +77,19 @@ export const modifiedChangedTextOverlayColor = registerColor( true ); -export const border = registerColor( - 'inlineEdit.border', +export const originalBorder = registerColor( + 'inlineEdit.originalBorder', + { + light: darken(editorLineHighlightBorder, 0.15), + dark: lighten(editorLineHighlightBorder, 0.50), + hcDark: editorLineHighlightBorder, + hcLight: editorLineHighlightBorder + }, + '' +); + +export const modifiedBorder = registerColor( + 'inlineEdit.modifiedBorder', { light: darken(editorLineHighlightBorder, 0.15), dark: lighten(editorLineHighlightBorder, 0.50), @@ -174,14 +185,14 @@ export class InlineEditsView extends Disposable { const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path1.setAttribute('d', pathBuilder1.build()); path1.style.fill = 'var(--vscode-inlineEdit-originalBackground, transparent)'; - path1.style.stroke = 'var(--vscode-inlineEdit-border)'; + path1.style.stroke = 'var(--vscode-inlineEdit-originalBorder)'; path1.style.strokeWidth = '1px'; const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path2.setAttribute('d', pathBuilder2.build()); path2.style.fill = 'var(--vscode-inlineEdit-modifiedBackground, transparent)'; - path2.style.stroke = 'var(--vscode-inlineEdit-border)'; + path2.style.stroke = 'var(--vscode-inlineEdit-modifiedBorder)'; path2.style.strokeWidth = '1px'; const pathModifiedBackground = document.createElementNS('http://www.w3.org/2000/svg', 'path'); @@ -201,11 +212,11 @@ export class InlineEditsView extends Disposable { const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); stop1.setAttribute('offset', '0%'); - stop1.setAttribute('style', 'stop-color:var(--vscode-inlineEdit-border);stop-opacity:0'); + stop1.setAttribute('style', 'stop-color:var(--vscode-inlineEdit-modifiedBorder);stop-opacity:0'); const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); stop2.setAttribute('offset', '100%'); - stop2.setAttribute('style', 'stop-color:var(--vscode-inlineEdit-border);stop-opacity:1'); + stop2.setAttribute('style', 'stop-color:var(--vscode-inlineEdit-modifiedBorder);stop-opacity:1'); linearGradient.appendChild(stop1); linearGradient.appendChild(stop2); @@ -230,7 +241,7 @@ export class InlineEditsView extends Disposable { const path3 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path3.setAttribute('d', pathBuilder3.build()); - path3.style.stroke = 'var(--vscode-inlineEdit-border)'; + path3.style.stroke = 'var(--vscode-inlineEdit-modifiedBorder)'; path3.style.strokeWidth = '1px'; elements.push(path3); } From 5fb587f8bc595065e163aa9ddef113c73211dc7b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 11 Dec 2024 11:02:36 -0800 Subject: [PATCH 115/479] Tweaks to sign up and 'retry' buttons (#235855) * Tweaks to sign up and 'retry' buttons * Update label --- .../chatContentParts/chatQuotaExceededPart.ts | 72 +++++++++++++------ .../contrib/chat/browser/media/chat.css | 10 ++- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts index e3e06c9b05a..da3c7ae323b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts @@ -22,6 +22,16 @@ import { IChatContentPart } from './chatContentParts.js'; const $ = dom.$; +/** + * Once the sign up button is clicked, and the retry button has been shown, it should be shown every time. + */ +let shouldShowRetryButton = false; + +/** + * Once the 'retry' button is clicked, the wait warning should be shown every time. + */ +let shouldShowWaitWarning = false; + export class ChatQuotaExceededPart extends Disposable implements IChatContentPart { public readonly domNode: HTMLElement; @@ -51,30 +61,52 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar button1.label = localize('upgradeToCopilotPro', "Upgrade to Copilot Pro"); button1.element.classList.add('chat-quota-error-button'); - let didAddSecondary = false; + let hasAddedWaitWarning = false; + const addWaitWarningIfNeeded = () => { + if (!shouldShowWaitWarning || hasAddedWaitWarning) { + return; + } + + hasAddedWaitWarning = true; + dom.append(messageContainer, $('.chat-quota-wait-warning', undefined, localize('waitWarning', "Signing up may take a few minutes to take effect."))); + }; + + let hasAddedRetryButton = false; + const addRetryButtonIfNeeded = () => { + if (!shouldShowRetryButton || hasAddedRetryButton) { + return; + } + + hasAddedRetryButton = true; + const button2 = this._register(new Button(messageContainer, { + buttonBackground: undefined, + buttonForeground: asCssVariable(textLinkForeground) + })); + button2.element.classList.add('chat-quota-error-secondary-button'); + button2.label = localize('signedUpClickToContinue', "Signed up? Click to retry"); + this._onDidChangeHeight.fire(); + this._register(button2.onDidClick(() => { + const widget = chatWidgetService.getWidgetBySessionId(element.sessionId); + if (!widget) { + return; + } + + widget.rerunLastRequest(); + + shouldShowWaitWarning = true; + addWaitWarningIfNeeded(); + })); + }; + this._register(button1.onDidClick(async () => { await commandService.executeCommand('workbench.action.chat.upgradePlan'); - if (!didAddSecondary) { - didAddSecondary = true; - - const button2 = this._register(new Button(messageContainer, { - buttonBackground: undefined, - buttonForeground: asCssVariable(textLinkForeground) - })); - button2.element.classList.add('chat-quota-error-secondary-button'); - button2.label = localize('signedUpClickToContinue', "Signed up? Click to continue!"); - this._onDidChangeHeight.fire(); - this._register(button2.onDidClick(() => { - const widget = chatWidgetService.getWidgetBySessionId(element.sessionId); - if (!widget) { - return; - } - - widget.rerunLastRequest(); - })); - } + shouldShowRetryButton = true; + addRetryButtonIfNeeded(); })); + + addRetryButtonIfNeeded(); + addWaitWarningIfNeeded(); } hasSameContent(other: unknown): boolean { diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index fdcc82938f1..b93329f8ddf 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -1474,11 +1474,19 @@ have to be updated for changes to the rules above, or to support more deeply nes .chat-quota-error-secondary-button { margin-top: 6px; - font-size: 12px; padding: 0px; border: none; } + .chat-quota-error-secondary-button, + .chat-quota-wait-warning { + font-size: 12px; + } + + .chat-quota-wait-warning { + margin-top: 2px; + } + .chat-quota-error-message { .rendered-markdown p { margin: 0px; From 079df18bc89a113d831a43fa36c2f472136d5b40 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 11 Dec 2024 20:17:42 +0100 Subject: [PATCH 116/479] Adding possibility to update all verbose hovers at once (#235848) adding possibility to update all verbose hovers at once --- .../standaloneColorPickerWidget.ts | 1 + .../hover/browser/contentHoverRendered.ts | 48 ++++++++++++++----- .../browser/contentHoverWidgetWrapper.ts | 3 +- .../contrib/hover/browser/hoverTypes.ts | 4 ++ .../hover/browser/markdownHoverParticipant.ts | 29 +++++------ 5 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts index c26966a842f..f15557441bd 100644 --- a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts @@ -179,6 +179,7 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW onContentsChanged: () => { }, setMinimumDimensions: () => { }, hide: () => this.hide(), + focus: () => this.focus() }; this._colorHover = colorHover; diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index dde3cb77bb6..81ccfcbc60b 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -22,6 +22,7 @@ import { InlayHintsHover } from '../../inlayHints/browser/inlayHintsHover.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; import { HoverAction } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IOffsetRange } from '../../../common/core/offsetRange.js'; export class RenderedContentHover extends Disposable { @@ -378,20 +379,35 @@ class RenderedContentHoverParts extends Disposable { if (!this._markdownHoverParticipant) { return; } - const normalizedMarkdownHoverIndex = this._normalizedIndexToMarkdownHoverIndexRange(this._markdownHoverParticipant, index); - if (normalizedMarkdownHoverIndex === undefined) { - return; + let rangeOfIndicesToUpdate: IOffsetRange; + if (index >= 0) { + rangeOfIndicesToUpdate = { start: index, endExclusive: index + 1 }; + } else { + rangeOfIndicesToUpdate = this._findRangeOfMarkdownHoverParts(this._markdownHoverParticipant); } - const renderedPart = await this._markdownHoverParticipant.updateMarkdownHoverVerbosityLevel(action, normalizedMarkdownHoverIndex, focus); - if (!renderedPart) { - return; + for (let i = rangeOfIndicesToUpdate.start; i < rangeOfIndicesToUpdate.endExclusive; i++) { + const normalizedMarkdownHoverIndex = this._normalizedIndexToMarkdownHoverIndexRange(this._markdownHoverParticipant, i); + if (normalizedMarkdownHoverIndex === undefined) { + continue; + } + const renderedPart = await this._markdownHoverParticipant.updateMarkdownHoverVerbosityLevel(action, normalizedMarkdownHoverIndex); + if (!renderedPart) { + continue; + } + this._renderedParts[i] = { + type: 'hoverPart', + participant: this._markdownHoverParticipant, + hoverPart: renderedPart.hoverPart, + hoverElement: renderedPart.hoverElement, + }; + } + if (focus) { + if (index >= 0) { + this.focusHoverPartWithIndex(index); + } else { + this._context.focus(); + } } - this._renderedParts[index] = { - type: 'hoverPart', - participant: this._markdownHoverParticipant, - hoverPart: renderedPart.hoverPart, - hoverElement: renderedPart.hoverElement, - }; this._context.onContentsChanged(); } @@ -429,6 +445,14 @@ class RenderedContentHoverParts extends Disposable { return index - firstIndexOfMarkdownHovers; } + private _findRangeOfMarkdownHoverParts(markdownHoverParticipant: MarkdownHoverParticipant): IOffsetRange { + const copiedRenderedParts = this._renderedParts.slice(); + const firstIndexOfMarkdownHovers = copiedRenderedParts.findIndex(renderedPart => renderedPart.type === 'hoverPart' && renderedPart.participant === markdownHoverParticipant); + const inversedLastIndexOfMarkdownHovers = copiedRenderedParts.reverse().findIndex(renderedPart => renderedPart.type === 'hoverPart' && renderedPart.participant === markdownHoverParticipant); + const lastIndexOfMarkdownHovers = inversedLastIndexOfMarkdownHovers >= 0 ? copiedRenderedParts.length - inversedLastIndexOfMarkdownHovers : inversedLastIndexOfMarkdownHovers; + return { start: firstIndexOfMarkdownHovers, endExclusive: lastIndexOfMarkdownHovers + 1 }; + } + public get domNode(): DocumentFragment { return this._fragment; } diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts index cd2fedb338d..ad9a9920967 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts @@ -231,7 +231,8 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge const setMinimumDimensions = (dimensions: dom.Dimension) => { this._contentHoverWidget.setMinimumDimensions(dimensions); }; - return { hide, onContentsChanged, setMinimumDimensions }; + const focus = () => this.focus(); + return { hide, onContentsChanged, setMinimumDimensions, focus }; } diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index a82edb31852..ad5442f1fa2 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -108,6 +108,10 @@ export interface IEditorHoverContext { * Hide the hover. */ hide(): void; + /** + * Focus the hover. + */ + focus(): void; } export interface IEditorHoverRenderContext extends IEditorHoverContext { diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index 481e7791355..3fd5876cfe4 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -204,8 +204,8 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant { - return Promise.resolve(this._renderedHoverParts?.updateMarkdownHoverPartVerbosityLevel(action, index, focus)); + public updateMarkdownHoverVerbosityLevel(action: HoverVerbosityAction, index: number): Promise<{ hoverPart: MarkdownHover; hoverElement: HTMLElement } | undefined> { + return Promise.resolve(this._renderedHoverParts?.updateMarkdownHoverPartVerbosityLevel(action, index)); } } @@ -327,13 +327,13 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { return store; } actionElement.classList.add('enabled'); - const actionFunction = () => this._commandService.executeCommand(action === HoverVerbosityAction.Increase ? INCREASE_HOVER_VERBOSITY_ACTION_ID : DECREASE_HOVER_VERBOSITY_ACTION_ID); + const actionFunction = () => this._commandService.executeCommand(action === HoverVerbosityAction.Increase ? INCREASE_HOVER_VERBOSITY_ACTION_ID : DECREASE_HOVER_VERBOSITY_ACTION_ID, { focus: true }); store.add(new ClickAction(actionElement, actionFunction)); store.add(new KeyDownAction(actionElement, actionFunction, [KeyCode.Enter, KeyCode.Space])); return store; } - public async updateMarkdownHoverPartVerbosityLevel(action: HoverVerbosityAction, index: number, focus: boolean = true): Promise<{ hoverPart: MarkdownHover; hoverElement: HTMLElement } | undefined> { + public async updateMarkdownHoverPartVerbosityLevel(action: HoverVerbosityAction, index: number): Promise<{ hoverPart: MarkdownHover; hoverElement: HTMLElement } | undefined> { const model = this._editor.getModel(); if (!model) { return undefined; @@ -357,13 +357,9 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { initialHoverPart.ordinal, newHoverSource ); - const newHoverRenderedPart = this._renderHoverPart( - newHoverPart, - this._onFinishedRendering - ); - this._replaceRenderedHoverPartAtIndex(index, newHoverRenderedPart, newHoverPart); - if (focus) { - this._focusOnHoverPartWithIndex(index); + const newHoverRenderedPart = this._updateRenderedHoverPart(index, newHoverPart); + if (!newHoverRenderedPart) { + return undefined; } return { hoverPart: newHoverPart, @@ -416,10 +412,11 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { return hover; } - private _replaceRenderedHoverPartAtIndex(index: number, renderedHoverPart: RenderedMarkdownHoverPart, hoverPart: MarkdownHover): void { + private _updateRenderedHoverPart(index: number, hoverPart: MarkdownHover): RenderedMarkdownHoverPart | undefined { if (index >= this.renderedHoverParts.length || index < 0) { - return; + return undefined; } + const renderedHoverPart = this._renderHoverPart(hoverPart, this._onFinishedRendering); const currentRenderedHoverPart = this.renderedHoverParts[index]; const currentRenderedMarkdown = currentRenderedHoverPart.hoverElement; const renderedMarkdown = renderedHoverPart.hoverElement; @@ -430,13 +427,9 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { currentRenderedMarkdown, renderedHoverPart.disposables ); - currentRenderedMarkdown.focus(); currentRenderedHoverPart.dispose(); this.renderedHoverParts[index] = newRenderedHoverPart; - } - - private _focusOnHoverPartWithIndex(index: number): void { - this.renderedHoverParts[index].hoverElement.focus(); + return newRenderedHoverPart; } private _getRenderedHoverPartAtIndex(index: number): RenderedMarkdownHoverPart | undefined { From 5da3e1776978225f199b6fa2df6ec92f320abc7e Mon Sep 17 00:00:00 2001 From: tobil4sk Date: Wed, 11 Dec 2024 20:51:18 +0000 Subject: [PATCH 117/479] Merge diverging findExecutable functions (#228373) --- src/vs/base/node/processes.ts | 105 +++++++++--------- .../node/externalTerminalService.ts | 3 +- .../terminal/node/terminalEnvironment.ts | 56 ---------- .../platform/terminal/node/terminalProcess.ts | 3 +- .../terminal/node/terminalProfiles.ts | 3 +- src/vs/workbench/api/node/extHostTask.ts | 23 ++-- .../tasks/browser/terminalTaskSystem.ts | 11 +- 7 files changed, 81 insertions(+), 123 deletions(-) diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index bbf15d0db8e..ea1cafb9748 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -5,6 +5,7 @@ import * as cp from 'child_process'; import { Stats, promises } from 'fs'; +import { getCaseInsensitive } from '../common/objects.js'; import * as path from '../common/path.js'; import * as Platform from '../common/platform.js'; import * as process from '../common/process.js'; @@ -64,58 +65,61 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender return { send }; } -export namespace win32 { - export async function findExecutable(command: string, cwd?: string, paths?: string[]): Promise { - // If we have an absolute path then we take it. - if (path.isAbsolute(command)) { - return command; - } - if (cwd === undefined) { - cwd = process.cwd(); - } - const dir = path.dirname(command); - if (dir !== '.') { - // We have a directory and the directory is relative (see above). Make the path absolute - // to the current working directory. - return path.join(cwd, command); - } - if (paths === undefined && Types.isString(process.env['PATH'])) { - paths = process.env['PATH'].split(path.delimiter); - } - // No PATH environment. Make path absolute to the cwd. - if (paths === undefined || paths.length === 0) { - return path.join(cwd, command); - } - - async function fileExists(path: string): Promise { - if (await pfs.Promises.exists(path)) { - let statValue: Stats | undefined; - try { - statValue = await promises.stat(path); - } catch (e) { - if (e.message.startsWith('EACCES')) { - // it might be symlink - statValue = await promises.lstat(path); - } - } - return statValue ? !statValue.isDirectory() : false; +async function fileExistsDefault(path: string): Promise { + if (await pfs.Promises.exists(path)) { + let statValue: Stats | undefined; + try { + statValue = await promises.stat(path); + } catch (e) { + if (e.message.startsWith('EACCES')) { + // it might be symlink + statValue = await promises.lstat(path); } - return false; } + return statValue ? !statValue.isDirectory() : false; + } + return false; +} - // We have a simple file name. We get the path variable from the env - // and try to find the executable on the path. - for (const pathEntry of paths) { - // The path entry is absolute. - let fullPath: string; - if (path.isAbsolute(pathEntry)) { - fullPath = path.join(pathEntry, command); - } else { - fullPath = path.join(cwd, pathEntry, command); - } - if (await fileExists(fullPath)) { - return fullPath; - } +export async function findExecutable(command: string, cwd?: string, paths?: string[], env: Platform.IProcessEnvironment = process.env as Platform.IProcessEnvironment, fileExists: (path: string) => Promise = fileExistsDefault): Promise { + // If we have an absolute path then we take it. + if (path.isAbsolute(command)) { + return await fileExists(command) ? command : undefined; + } + if (cwd === undefined) { + cwd = process.cwd(); + } + const dir = path.dirname(command); + if (dir !== '.') { + // We have a directory and the directory is relative (see above). Make the path absolute + // to the current working directory. + const fullPath = path.join(cwd, command); + return await fileExists(fullPath) ? fullPath : undefined; + } + const envPath = getCaseInsensitive(env, 'PATH'); + if (paths === undefined && Types.isString(envPath)) { + paths = envPath.split(path.delimiter); + } + // No PATH environment. Make path absolute to the cwd. + if (paths === undefined || paths.length === 0) { + const fullPath = path.join(cwd, command); + return await fileExists(fullPath) ? fullPath : undefined; + } + + // We have a simple file name. We get the path variable from the env + // and try to find the executable on the path. + for (const pathEntry of paths) { + // The path entry is absolute. + let fullPath: string; + if (path.isAbsolute(pathEntry)) { + fullPath = path.join(pathEntry, command); + } else { + fullPath = path.join(cwd, pathEntry, command); + } + if (await fileExists(fullPath)) { + return fullPath; + } + if (Platform.isWindows) { let withExtension = fullPath + '.com'; if (await fileExists(withExtension)) { return withExtension; @@ -125,6 +129,7 @@ export namespace win32 { return withExtension; } } - return path.join(cwd, command); } + const fullPath = path.join(cwd, command); + return await fileExists(fullPath) ? fullPath : undefined; } diff --git a/src/vs/platform/externalTerminal/node/externalTerminalService.ts b/src/vs/platform/externalTerminal/node/externalTerminalService.ts index 47c12711f8c..ca6c82b31d7 100644 --- a/src/vs/platform/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/platform/externalTerminal/node/externalTerminalService.ts @@ -134,8 +134,7 @@ export class WindowsExternalTerminalService extends ExternalTerminalService impl @memoize private static async getWtExePath() { try { - const wtPath = await processes.win32.findExecutable('wt'); - return await pfs.Promises.exists(wtPath) ? wtPath : undefined; + return await processes.findExecutable('wt'); } catch { return undefined; } diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts index 3d6cdfc82cf..3688900dc2b 100644 --- a/src/vs/platform/terminal/node/terminalEnvironment.ts +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -5,13 +5,10 @@ import * as os from 'os'; import { FileAccess } from '../../../base/common/network.js'; -import { getCaseInsensitive } from '../../../base/common/objects.js'; import * as path from '../../../base/common/path.js'; import { IProcessEnvironment, isMacintosh, isWindows } from '../../../base/common/platform.js'; import * as process from '../../../base/common/process.js'; import { format } from '../../../base/common/strings.js'; -import { isString } from '../../../base/common/types.js'; -import * as pfs from '../../../base/node/pfs.js'; import { ILogService } from '../../log/common/log.js'; import { IProductService } from '../../product/common/productService.js'; import { IShellLaunchConfig, ITerminalEnvironment, ITerminalProcessOptions } from '../common/terminal.js'; @@ -28,59 +25,6 @@ export function getWindowsBuildNumber(): number { return buildNumber; } -export async function findExecutable(command: string, cwd?: string, paths?: string[], env: IProcessEnvironment = process.env as IProcessEnvironment, exists: (path: string) => Promise = pfs.Promises.exists): Promise { - // If we have an absolute path then we take it. - if (path.isAbsolute(command)) { - return await exists(command) ? command : undefined; - } - if (cwd === undefined) { - cwd = process.cwd(); - } - const dir = path.dirname(command); - if (dir !== '.') { - // We have a directory and the directory is relative (see above). Make the path absolute - // to the current working directory. - const fullPath = path.join(cwd, command); - return await exists(fullPath) ? fullPath : undefined; - } - const envPath = getCaseInsensitive(env, 'PATH'); - if (paths === undefined && isString(envPath)) { - paths = envPath.split(path.delimiter); - } - // No PATH environment. Make path absolute to the cwd. - if (paths === undefined || paths.length === 0) { - const fullPath = path.join(cwd, command); - return await exists(fullPath) ? fullPath : undefined; - } - // We have a simple file name. We get the path variable from the env - // and try to find the executable on the path. - for (const pathEntry of paths) { - // The path entry is absolute. - let fullPath: string; - if (path.isAbsolute(pathEntry)) { - fullPath = path.join(pathEntry, command); - } else { - fullPath = path.join(cwd, pathEntry, command); - } - - if (await exists(fullPath)) { - return fullPath; - } - if (isWindows) { - let withExtension = fullPath + '.com'; - if (await exists(withExtension)) { - return withExtension; - } - withExtension = fullPath + '.exe'; - if (await exists(withExtension)) { - return withExtension; - } - } - } - const fullPath = path.join(cwd, command); - return await exists(fullPath) ? fullPath : undefined; -} - export interface IShellIntegrationConfigInjection { /** * A new set of arguments to use. diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index d7a470d5bf6..a57acc78b17 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -10,13 +10,14 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, toDisposable } from '../../../base/common/lifecycle.js'; import * as path from '../../../base/common/path.js'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from '../../../base/common/platform.js'; +import { findExecutable } from '../../../base/node/processes.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { ILogService, LogLevel } from '../../log/common/log.js'; import { IProductService } from '../../product/common/productService.js'; import { FlowControlConstants, IShellLaunchConfig, ITerminalChildProcess, ITerminalLaunchError, IProcessProperty, IProcessPropertyMap as IProcessPropertyMap, ProcessPropertyType, TerminalShellType, IProcessReadyEvent, ITerminalProcessOptions, PosixShellType, IProcessReadyWindowsPty, GeneralShellType } from '../common/terminal.js'; import { ChildProcessMonitor } from './childProcessMonitor.js'; -import { findExecutable, getShellIntegrationInjection, getWindowsBuildNumber, IShellIntegrationConfigInjection } from './terminalEnvironment.js'; +import { getShellIntegrationInjection, getWindowsBuildNumber, IShellIntegrationConfigInjection } from './terminalEnvironment.js'; import { WindowsShellHelper } from './windowsShellHelper.js'; import { IPty, IPtyForkOptions, IWindowsPtyForkOptions, spawn } from 'node-pty'; import { chunkInput } from '../common/terminalProcess.js'; diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index 76e77d9174c..fd5d14a71fb 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -8,6 +8,7 @@ import * as cp from 'child_process'; import { Codicon } from '../../../base/common/codicons.js'; import { basename, delimiter, normalize } from '../../../base/common/path.js'; import { isLinux, isWindows } from '../../../base/common/platform.js'; +import { findExecutable } from '../../../base/node/processes.js'; import { isString } from '../../../base/common/types.js'; import { URI } from '../../../base/common/uri.js'; import * as pfs from '../../../base/node/pfs.js'; @@ -15,7 +16,7 @@ import { enumeratePowerShellInstallations } from '../../../base/node/powershell. import { IConfigurationService } from '../../configuration/common/configuration.js'; import { ILogService } from '../../log/common/log.js'; import { ITerminalEnvironment, ITerminalExecutable, ITerminalProfile, ITerminalProfileSource, ITerminalUnsafePath, ProfileSource, TerminalIcon, TerminalSettingId } from '../common/terminal.js'; -import { findExecutable, getWindowsBuildNumber } from './terminalEnvironment.js'; +import { getWindowsBuildNumber } from './terminalEnvironment.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { dirname, resolve } from 'path'; diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 1e1f0637a26..b6cfc881ec4 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -6,7 +6,7 @@ import * as path from '../../../base/common/path.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { win32 } from '../../../base/node/processes.js'; +import { findExecutable } from '../../../base/node/processes.js'; import * as types from '../common/extHostTypes.js'; import { IExtHostWorkspace } from '../common/extHostWorkspace.js'; import type * as vscode from 'vscode'; @@ -144,7 +144,7 @@ export class ExtHostTask extends ExtHostTaskBase { public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }; variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }> { const uri: URI = URI.revive(uriComponents); const result = { - process: undefined as string, + process: undefined as string | undefined, variables: Object.create(null) }; const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri); @@ -171,11 +171,16 @@ export class ExtHostTask extends ExtHostTaskBase { paths[i] = await resolver.resolveAsync(ws, paths[i]); } } - result.process = await win32.findExecutable( - await resolver.resolveAsync(ws, toResolve.process.name), - toResolve.process.cwd !== undefined ? await resolver.resolveAsync(ws, toResolve.process.cwd) : undefined, - paths - ); + const processName = await resolver.resolveAsync(ws, toResolve.process.name); + const cwd = toResolve.process.cwd !== undefined ? await resolver.resolveAsync(ws, toResolve.process.cwd) : undefined; + const foundExecutable = await findExecutable(processName, cwd, paths); + if (foundExecutable) { + result.process = foundExecutable; + } else if (path.isAbsolute(processName)) { + result.process = processName; + } else { + result.process = path.join(cwd ?? '', processName); + } } return result; } @@ -184,7 +189,7 @@ export class ExtHostTask extends ExtHostTaskBase { return true; } - public async $findExecutable(command: string, cwd?: string, paths?: string[]): Promise { - return win32.findExecutable(command, cwd, paths); + public async $findExecutable(command: string, cwd?: string, paths?: string[]): Promise { + return findExecutable(command, cwd, paths); } } diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index db362d7fb92..045a9e63b44 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -620,11 +620,14 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const command = await this._configurationResolverService.resolveAsync(workspaceFolder, CommandString.value(task.command.name!)); cwd = cwd ? await this._configurationResolverService.resolveAsync(workspaceFolder, cwd) : undefined; const paths = envPath ? await Promise.all(envPath.split(path.delimiter).map(p => this._configurationResolverService.resolveAsync(workspaceFolder, p))) : undefined; - let foundExecutable = await systemInfo?.findExecutable(command, cwd, paths); - if (!foundExecutable) { - foundExecutable = path.join(cwd ?? '', command); + const foundExecutable = await systemInfo?.findExecutable(command, cwd, paths); + if (foundExecutable) { + return foundExecutable; } - return foundExecutable; + if (path.isAbsolute(command)) { + return command; + } + return path.join(cwd ?? '', command); } private _findUnresolvedVariables(variables: Set, alreadyResolved: Map): Set { From 0d84cd0d8da60b395d57894fc6cf9ad135aca942 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 11 Dec 2024 21:58:34 +0100 Subject: [PATCH 118/479] chat - setup tweaks (#235861) --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index d23c468f229..ee5c1d8ff0b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -824,7 +824,7 @@ class ChatSetupWelcomeContent extends Disposable { // Header { - const header = localize({ key: 'setupHeader', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer.", this.context.state.installed ? 'command:github.copilot.open.walkthrough' : defaultChat.documentationUrl); + const header = localize({ key: 'header', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer.", this.context.state.installed ? 'command:github.copilot.open.walkthrough' : defaultChat.documentationUrl); this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(header, { isTrusted: true }))).element); const features = this.element.appendChild($('div.chat-features-container')); @@ -850,7 +850,7 @@ class ChatSetupWelcomeContent extends Disposable { } // Limited SKU - const free = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); + const free = localize({ key: 'free', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); const freeContainer = this.element.appendChild($('p')); freeContainer.appendChild(this._register(markdown.render(new MarkdownString(free, { isTrusted: true, supportThemeIcons: true }))).element); @@ -875,11 +875,11 @@ class ChatSetupWelcomeContent extends Disposable { this._register(button.onDidClick(() => this.controller.setup())); // Terms - const terms = localize({ key: 'termsLabel', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); + 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); this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element); // SKU Settings - const settings = localize({ key: 'termsLabelWithTelemetry', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot may show [public code]({0}) suggestions and collect usage data. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); + const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot may show [public code]({0}) suggestions and collect telemetry. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); const settingsContainer = this.element.appendChild($('p')); settingsContainer.appendChild(this._register(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element); From c5b2d919e3d59e83257adacd04f08527621fdad9 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:59:59 +0100 Subject: [PATCH 119/479] Enable visibility toggling for terminal and other views (#235846) fix #141405 --- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 2 +- .../workbench/contrib/markers/browser/markers.contribution.ts | 2 +- src/vs/workbench/contrib/output/browser/output.contribution.ts | 2 +- .../workbench/contrib/terminal/browser/terminal.contribution.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 06d29a3a774..7c501db354f 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -395,7 +395,7 @@ Registry.as(ViewExtensions.ViewsRegistry).registerViews([{ id: REPL_VIEW_ID, name: nls.localize2({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, "Debug Console"), containerIcon: icons.debugConsoleViewIcon, - canToggleVisibility: false, + canToggleVisibility: true, canMoveView: true, when: CONTEXT_DEBUGGERS_AVAILABLE, ctorDescriptor: new SyncDescriptor(Repl), diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 40b664bc78d..6543d59ee29 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -140,7 +140,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews id: Markers.MARKERS_VIEW_ID, containerIcon: markersViewIcon, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, - canToggleVisibility: false, + canToggleVisibility: true, canMoveView: true, ctorDescriptor: new SyncDescriptor(MarkersView), openCommandActionDescriptor: { diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index a06d596b9b0..7288357d606 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -72,7 +72,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews name: nls.localize2('output', "Output"), containerIcon: outputViewIcon, canMoveView: true, - canToggleVisibility: false, + canToggleVisibility: true, ctorDescriptor: new SyncDescriptor(OutputViewPane), openCommandActionDescriptor: { id: 'workbench.action.output.toggleOutput', diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 452daef0e18..cee30a6d28b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -116,7 +116,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews id: TERMINAL_VIEW_ID, name: nls.localize2('terminal', "Terminal"), containerIcon: terminalViewIcon, - canToggleVisibility: false, + canToggleVisibility: true, canMoveView: true, ctorDescriptor: new SyncDescriptor(TerminalViewPane), openCommandActionDescriptor: { From 605672003f4881813e9a426efc520621ab9f8a5a Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 11 Dec 2024 22:09:33 +0100 Subject: [PATCH 120/479] Erroneous description for setting "CSS Format: Preseve New Lines" (#235864) --- extensions/css-language-features/package.nls.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index d6e25a57a43..057ec214bc2 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -35,7 +35,7 @@ "css.format.newlineBetweenRules.desc": "Separate rulesets by a blank line.", "css.format.spaceAroundSelectorSeparator.desc": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`).", "css.format.braceStyle.desc": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`).", - "css.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved.", + "css.format.preserveNewLines.desc": "Whether existing line breaks before rules and declarations should be preserved.", "css.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk, when `#css.format.preserveNewLines#` is enabled.", "less.title": "LESS", "less.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", @@ -69,7 +69,7 @@ "less.format.newlineBetweenRules.desc": "Separate rulesets by a blank line.", "less.format.spaceAroundSelectorSeparator.desc": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`).", "less.format.braceStyle.desc": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`).", - "less.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved.", + "less.format.preserveNewLines.desc": "Whether existing line breaks before rules and declarations should be preserved.", "less.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk, when `#less.format.preserveNewLines#` is enabled.", "scss.title": "SCSS (Sass)", "scss.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", @@ -103,7 +103,7 @@ "scss.format.newlineBetweenRules.desc": "Separate rulesets by a blank line.", "scss.format.spaceAroundSelectorSeparator.desc": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`).", "scss.format.braceStyle.desc": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`).", - "scss.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved.", + "scss.format.preserveNewLines.desc": "Whether existing line breaks before rules and declarations should be preserved.", "scss.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk, when `#scss.format.preserveNewLines#` is enabled.", "css.colorDecorators.enable.deprecationMessage": "The setting `css.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", "scss.colorDecorators.enable.deprecationMessage": "The setting `scss.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", From 3912f7a2b7fd1f54fde675d2462b54b719073cc7 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Wed, 11 Dec 2024 14:59:27 -0800 Subject: [PATCH 121/479] chore: place assignee in its own variable (#235866) --- .vscode/notebooks/grooming.github-issues | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 6e2e81b21a5..4b2f8c6750f 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\r\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\r\n" + "value": "// list of repos we work in\r\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\r\n\r\n$assignee=@me\r\n" }, { "kind": 1, @@ -17,7 +17,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r\n" + "value": "$repos assignee:$assignee is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r\n" }, { "kind": 1, @@ -27,7 +27,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:audio-cue -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering" + "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:audio-cue -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering" }, { "kind": 1, @@ -37,7 +37,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed -label:confirmation-pending -label:under-discussion\r\n" + "value": "$repos assignee:$assignee is:open type:issue no:milestone -label:info-needed -label:triage-needed -label:confirmation-pending -label:under-discussion\r\n" }, { "kind": 1, @@ -47,6 +47,6 @@ { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:\"info-needed\"\r\n" + "value": "$repos assignee:$assignee is:open label:\"info-needed\"\r\n" } ] \ No newline at end of file From e095f631eb35af4fa32d39f0578184208f49ff15 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 11 Dec 2024 15:04:36 -0800 Subject: [PATCH 122/479] Add period to new button label (#235872) --- .../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 da3c7ae323b..97985ca4969 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts @@ -83,7 +83,7 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar buttonForeground: asCssVariable(textLinkForeground) })); button2.element.classList.add('chat-quota-error-secondary-button'); - button2.label = localize('signedUpClickToContinue', "Signed up? Click to retry"); + button2.label = localize('signedUpClickToContinue', "Signed up? Click to retry."); this._onDidChangeHeight.fire(); this._register(button2.onDidClick(() => { const widget = chatWidgetService.getWidgetBySessionId(element.sessionId); From 9fbfe5db10740b5ac96d89268afc8b4467879d26 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 12 Dec 2024 01:24:32 +0100 Subject: [PATCH 123/479] Add okLabel to pick input (#235744) Fixes #194519 --- src/vs/platform/quickinput/browser/quickInput.ts | 11 +++++++++++ src/vs/platform/quickinput/common/quickInput.ts | 5 +++++ .../services/dialogs/browser/simpleFileDialog.ts | 1 + 3 files changed, 17 insertions(+) diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 6d7096e85ea..432416a5a11 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -562,6 +562,7 @@ export class QuickPick | undefined; private valueSelectionUpdated = true; private _ok: boolean | 'default' = 'default'; + private _okLabel: string | undefined; private _customButton = false; private _customButtonLabel: string | undefined; private _customButtonHover: string | undefined; @@ -812,6 +813,15 @@ export class QuickPick 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) { this.filePickBox.customButton = true; this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local'); From e38f760f3d66339120c9665e27e168c44a84e2ff Mon Sep 17 00:00:00 2001 From: RedCMD Date: Thu, 12 Dec 2024 14:04:59 +1300 Subject: [PATCH 124/479] Fix extension preview codeblock language getter --- .../markdown/browser/markdownDocumentRenderer.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts index 30f5fd50407..e80722d7993 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts @@ -234,7 +234,7 @@ export async function renderMarkdownDocument( namespace MarkedHighlight { // Copied from https://github.com/markedjs/marked-highlight/blob/main/src/index.js - export function markedHighlight(options: marked.MarkedOptions & { highlight: (code: string, lang: string, info: string) => string | Promise }): marked.MarkedExtension { + export function markedHighlight(options: marked.MarkedOptions & { highlight: (code: string, lang: string) => string | Promise }): marked.MarkedExtension { if (typeof options === 'function') { options = { highlight: options, @@ -252,13 +252,11 @@ namespace MarkedHighlight { return; } - const lang = getLang(token.lang); - if (options.async) { - return Promise.resolve(options.highlight(token.text, lang, token.lang || '')).then(updateToken(token)); + return Promise.resolve(options.highlight(token.text, token.lang)).then(updateToken(token)); } - const code = options.highlight(token.text, lang, token.lang || ''); + const code = options.highlight(token.text, token.lang); if (code instanceof Promise) { throw new Error('markedHighlight is not set to async but the highlight function is async. Set the async option to true on markedHighlight to await the async highlight function.'); } @@ -276,10 +274,6 @@ namespace MarkedHighlight { }; } - function getLang(lang: string) { - return (lang || '').match(/\S*/)![0]; - } - function updateToken(token: any) { return (code: string) => { if (typeof code === 'string' && code !== token.text) { From 6a5c8cf95aa814b5cb92de617096144b1f45af2d Mon Sep 17 00:00:00 2001 From: jaymroy <90058409+jaymroy@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:48:03 -0500 Subject: [PATCH 125/479] Issue: #214481 Add Option to Ignore Code Blocks in Text-to-Speech (#235697) * Add Option to Ignore Code Blocks in Text-to-Speech * Add Option to Ignore Code Blocks in Text-to-Speech * clean up --------- Co-authored-by: Benjamin Pasero Co-authored-by: Benjamin Pasero --- .../browser/accessibilityConfiguration.ts | 6 ++++ .../actions/voiceChatActions.ts | 31 +++++++++++++++++-- .../contrib/speech/common/speechService.ts | 1 + 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 9e64c7226d7..840442b7bac 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -789,6 +789,12 @@ export class DynamicSpeechAccessibilityConfiguration extends Disposable implemen 'minimum': 0, 'tags': ['accessibility'] }, + [AccessibilityVoiceSettingId.IgnoreCodeBlocks]: { + 'markdownDescription': localize('voice.ignoreCodeBlocks', "Whether to ignore code snippets in text-to-speech synthesis."), + 'type': 'boolean', + 'default': false, + 'tags': ['accessibility'] + }, [AccessibilityVoiceSettingId.SpeechLanguage]: { 'markdownDescription': localize('voice.speechLanguage', "The language that text-to-speech and speech-to-text should use. Select `auto` to use the configured display language if possible. Note that not all display languages maybe supported by speech recognition and synthesizers."), 'type': 'string', diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 198a49bb869..1b55c9d89df 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -707,6 +707,11 @@ class ChatSynthesizerSessionController { } } +interface IChatSyntheSizerContext { + readonly ignoreCodeBlocks: boolean; + insideCodeBlock: boolean; +} + class ChatSynthesizerSessions { private static instance: ChatSynthesizerSessions | undefined = undefined; @@ -722,6 +727,7 @@ class ChatSynthesizerSessions { constructor( @ISpeechService private readonly speechService: ISpeechService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { } @@ -768,11 +774,16 @@ class ChatSynthesizerSessions { } private async *nextChatResponseChunk(response: IChatResponseModel, token: CancellationToken): AsyncIterable { + const context: IChatSyntheSizerContext = { + ignoreCodeBlocks: this.configurationService.getValue(AccessibilityVoiceSettingId.IgnoreCodeBlocks), + insideCodeBlock: false + }; + let totalOffset = 0; let complete = false; do { const responseLength = response.response.toString().length; - const { chunk, offset } = this.parseNextChatResponseChunk(response, totalOffset); + const { chunk, offset } = this.parseNextChatResponseChunk(response, totalOffset, context); totalOffset = offset; complete = response.isComplete; @@ -790,7 +801,7 @@ class ChatSynthesizerSessions { } while (!token.isCancellationRequested && !complete); } - private parseNextChatResponseChunk(response: IChatResponseModel, offset: number): { readonly chunk: string | undefined; readonly offset: number } { + private parseNextChatResponseChunk(response: IChatResponseModel, offset: number, context: IChatSyntheSizerContext): { readonly chunk: string | undefined; readonly offset: number } { let chunk: string | undefined = undefined; const text = response.response.toString(); @@ -804,12 +815,28 @@ class ChatSynthesizerSessions { offset = res.offset; } + if (chunk && context.ignoreCodeBlocks) { + chunk = this.filterCodeBlocks(chunk, context); + } + return { chunk: chunk ? renderStringAsPlaintext({ value: chunk }) : chunk, // convert markdown to plain text offset }; } + private filterCodeBlocks(chunk: string, context: IChatSyntheSizerContext): string { + return chunk.split('\n') + .filter(line => { + if (line.trimStart().startsWith('```')) { + context.insideCodeBlock = !context.insideCodeBlock; + return false; + } + return !context.insideCodeBlock; + }) + .join('\n'); + } + stop(): void { this.activeSession?.dispose(true); this.activeSession = undefined; diff --git a/src/vs/workbench/contrib/speech/common/speechService.ts b/src/vs/workbench/contrib/speech/common/speechService.ts index 4df439533b5..03c9d1a305b 100644 --- a/src/vs/workbench/contrib/speech/common/speechService.ts +++ b/src/vs/workbench/contrib/speech/common/speechService.ts @@ -138,6 +138,7 @@ export const enum AccessibilityVoiceSettingId { SpeechTimeout = 'accessibility.voice.speechTimeout', AutoSynthesize = 'accessibility.voice.autoSynthesize', SpeechLanguage = 'accessibility.voice.speechLanguage', + IgnoreCodeBlocks = 'accessibility.voice.ignoreCodeBlocks' } export const SPEECH_LANGUAGE_CONFIG = AccessibilityVoiceSettingId.SpeechLanguage; From 2c7408dac485f923fefa6c9e8833f94f3a60261b Mon Sep 17 00:00:00 2001 From: Tomer Chachamu Date: Thu, 12 Dec 2024 05:13:30 +0000 Subject: [PATCH 126/479] Fix revealing a notebook cell from the debugger stopping and revealing active statement (Fixes #225290) (#225292) Co-authored-by: Don Jayamanne --- .../contrib/notebook/common/notebookEditorInput.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index e6fdd481218..92c066a28e7 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as glob from '../../../../base/common/glob.js'; -import { GroupIdentifier, ISaveOptions, IMoveResult, IRevertOptions, EditorInputCapabilities, Verbosity, IUntypedEditorInput, IFileLimitedEditorInputOptions } from '../../../common/editor.js'; +import { GroupIdentifier, ISaveOptions, IMoveResult, IRevertOptions, EditorInputCapabilities, Verbosity, IUntypedEditorInput, IFileLimitedEditorInputOptions, isResourceEditorInput } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { INotebookService, SimpleNotebookProviderInfo } from './notebookService.js'; import { URI } from '../../../../base/common/uri.js'; @@ -13,7 +13,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { INotebookEditorModelResolverService } from './notebookEditorModelResolverService.js'; import { IDisposable, IReference } from '../../../../base/common/lifecycle.js'; -import { CellEditType, IResolvedNotebookEditorModel } from './notebookCommon.js'; +import { CellEditType, CellUri, IResolvedNotebookEditorModel } from './notebookCommon.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { Schemas } from '../../../../base/common/network.js'; import { IFileService } from '../../../../platform/files/common/files.js'; @@ -358,6 +358,9 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { if (otherInput instanceof NotebookEditorInput) { return this.viewType === otherInput.viewType && isEqual(this.resource, otherInput.resource); } + if (isResourceEditorInput(otherInput) && otherInput.resource.scheme === CellUri.scheme) { + return isEqual(this.resource, CellUri.parse(otherInput.resource)?.notebook); + } return false; } } From ebc661ee13acc1f00ca1223a93bf931e9f5d6491 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 12 Dec 2024 08:54:11 +0100 Subject: [PATCH 127/479] Rename IChatSyntheSizerContext to IChatSynthesizerContext for consistency (#235897) --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 1b55c9d89df..568f6a693cc 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -707,7 +707,7 @@ class ChatSynthesizerSessionController { } } -interface IChatSyntheSizerContext { +interface IChatSynthesizerContext { readonly ignoreCodeBlocks: boolean; insideCodeBlock: boolean; } @@ -774,7 +774,7 @@ class ChatSynthesizerSessions { } private async *nextChatResponseChunk(response: IChatResponseModel, token: CancellationToken): AsyncIterable { - const context: IChatSyntheSizerContext = { + const context: IChatSynthesizerContext = { ignoreCodeBlocks: this.configurationService.getValue(AccessibilityVoiceSettingId.IgnoreCodeBlocks), insideCodeBlock: false }; @@ -801,7 +801,7 @@ class ChatSynthesizerSessions { } while (!token.isCancellationRequested && !complete); } - private parseNextChatResponseChunk(response: IChatResponseModel, offset: number, context: IChatSyntheSizerContext): { readonly chunk: string | undefined; readonly offset: number } { + private parseNextChatResponseChunk(response: IChatResponseModel, offset: number, context: IChatSynthesizerContext): { readonly chunk: string | undefined; readonly offset: number } { let chunk: string | undefined = undefined; const text = response.response.toString(); @@ -825,7 +825,7 @@ class ChatSynthesizerSessions { }; } - private filterCodeBlocks(chunk: string, context: IChatSyntheSizerContext): string { + private filterCodeBlocks(chunk: string, context: IChatSynthesizerContext): string { return chunk.split('\n') .filter(line => { if (line.trimStart().startsWith('```')) { From 9b22156745c792eaffaa39d94b7dc88c259ef8b9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 12 Dec 2024 09:24:43 +0100 Subject: [PATCH 128/479] chat - setup settings tweaks (#235900) --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index ee5c1d8ff0b..ba55f935c5e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -879,7 +879,7 @@ class ChatSetupWelcomeContent extends Disposable { this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element); // SKU Settings - const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot may show [public code]({0}) suggestions and collect telemetry. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); + const settings = 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); const settingsContainer = this.element.appendChild($('p')); settingsContainer.appendChild(this._register(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element); From c8379a73fffb9e4ed5ccb753e91611c6307e6d9f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 12 Dec 2024 09:47:48 +0100 Subject: [PATCH 129/479] Fix punctuation in quota exceeded messages for clarity (#235906) --- src/vs/workbench/contrib/chat/browser/chatQuotasService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index 87b4eb19f7f..51cee3588ca 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -119,9 +119,9 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService let message: string; const { chatQuotaExceeded, completionsQuotaExceeded } = that.quotas; if (chatQuotaExceeded && !completionsQuotaExceeded) { - message = localize('chatQuotaExceeded', "You've run out of free chat messages. You still have free code completions available in the Copilot Free plan. These limits will reset on {0}", dateFormatter.format(that.quotas.quotaResetDate)); + message = localize('chatQuotaExceeded', "You've run out of free chat messages. You still have free code completions available in the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(that.quotas.quotaResetDate)); } else if (completionsQuotaExceeded && !chatQuotaExceeded) { - message = localize('completionsQuotaExceeded', "You've run out of free code completions. You still have free chat messages available in the Copilot Free plan. These limits will reset on {0}", dateFormatter.format(that.quotas.quotaResetDate)); + message = localize('completionsQuotaExceeded', "You've run out of free code completions. You still have free chat messages available in the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(that.quotas.quotaResetDate)); } else { message = localize('chatAndCompletionsQuotaExceeded', "You've reached the limit of the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(that.quotas.quotaResetDate)); } From 516c29d18e1dd58359a4c7d38bd5df6cd00512ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 12 Dec 2024 09:50:44 +0100 Subject: [PATCH 130/479] fixes #235895 (#235905) --- src/vs/workbench/contrib/update/browser/update.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 2ff75f4594c..d824df32c93 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -259,13 +259,13 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu if (state.type === StateType.AvailableForDownload || state.type === StateType.Downloaded || state.type === StateType.Ready) { badge = new NumberBadge(1, () => nls.localize('updateIsReady', "New {0} update available.", this.productService.nameShort)); } else if (state.type === StateType.CheckingForUpdates) { - badge = new ProgressBadge(() => nls.localize('checkingForUpdates', "Checking for Updates...")); + badge = new ProgressBadge(() => nls.localize('checkingForUpdates', "Checking for {0} updates...", this.productService.nameShort)); priority = 1; } else if (state.type === StateType.Downloading) { - badge = new ProgressBadge(() => nls.localize('downloading', "Downloading...")); + badge = new ProgressBadge(() => nls.localize('downloading', "Downloading {0} update...", this.productService.nameShort)); priority = 1; } else if (state.type === StateType.Updating) { - badge = new ProgressBadge(() => nls.localize('updating', "Updating...")); + badge = new ProgressBadge(() => nls.localize('updating', "Updating {0}...", this.productService.nameShort)); priority = 1; } @@ -427,7 +427,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu group: '7_update', command: { id: 'update.checking', - title: nls.localize('checkingForUpdates', "Checking for Updates..."), + title: nls.localize('checkingForUpdates2', "Checking for Updates..."), precondition: ContextKeyExpr.false() }, when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.CheckingForUpdates) From 82d1ef937686fcb7bca342410f9071f84fbdb3a1 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 12 Dec 2024 10:42:39 +0100 Subject: [PATCH 131/479] Render expanded hovers always on the same side, so does not jump around (#235851) * add code always rendered on one side * update size after contents change * not allowing recomputation on verbosity request --- .../editor/contrib/hover/browser/contentHoverRendered.ts | 2 +- src/vs/editor/contrib/hover/browser/contentHoverWidget.ts | 6 ++++-- .../contrib/hover/browser/contentHoverWidgetWrapper.ts | 6 +++--- src/vs/editor/contrib/hover/browser/hoverTypes.ts | 6 +++++- .../contrib/hover/browser/markdownHoverParticipant.ts | 8 ++++---- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index 81ccfcbc60b..32d72a6693e 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -408,7 +408,7 @@ class RenderedContentHoverParts extends Disposable { this._context.focus(); } } - this._context.onContentsChanged(); + this._context.onContentsChanged({ allowPositionPreferenceRecomputation: false }); } public doesHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts index ffb8aaa9f11..063e4d108fe 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts @@ -17,6 +17,7 @@ import { getHoverAccessibleViewHint, HoverWidget } from '../../../../base/browse import { PositionAffinity } from '../../../common/model.js'; import { Emitter } from '../../../../base/common/event.js'; import { RenderedContentHover } from './contentHoverRendered.js'; +import { IContentsChangeOptions } from './hoverTypes.js'; const HORIZONTAL_SCROLLING_BY = 30; @@ -392,7 +393,7 @@ export class ContentHoverWidget extends ResizableContentWidget { this._resizableNode.minSize = new dom.Dimension(width, this._minimumSize.height); } - public onContentsChanged(): void { + public onContentsChanged(opts: IContentsChangeOptions = { allowPositionPreferenceRecomputation: true }): void { this._removeConstraintsRenderNormally(); const contentsDomNode = this._hover.contentsDomNode; @@ -407,8 +408,9 @@ export class ContentHoverWidget extends ResizableContentWidget { this._contentWidth = width; this._updateMinimumWidth(); this._resizableNode.layout(height, width); + this._updateResizableNodeMaxDimensions(); - if (this._renderedHover?.showAtPosition) { + if (opts.allowPositionPreferenceRecomputation && this._renderedHover?.showAtPosition) { const widgetHeight = dom.getTotalHeight(this._hover.containerDomNode); this._positionPreference = this._findPositionPreference(widgetHeight, this._renderedHover.showAtPosition); } diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts index ad9a9920967..d8439510336 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts @@ -11,7 +11,7 @@ import { EditorOption } from '../../../common/config/editorOptions.js'; import { Range } from '../../../common/core/range.js'; import { TokenizationRegistry } from '../../../common/languages.js'; import { HoverOperation, HoverResult, HoverStartMode, HoverStartSource } from './hoverOperation.js'; -import { HoverAnchor, HoverParticipantRegistry, HoverRangeAnchor, IEditorHoverContext, IEditorHoverParticipant, IHoverPart, IHoverWidget } from './hoverTypes.js'; +import { HoverAnchor, HoverParticipantRegistry, HoverRangeAnchor, IContentsChangeOptions, IEditorHoverContext, IEditorHoverParticipant, IHoverPart, IHoverWidget } from './hoverTypes.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { HoverVerbosityAction } from '../../../common/standalone/standaloneEnums.js'; @@ -224,9 +224,9 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge const hide = () => { this.hide(); }; - const onContentsChanged = () => { + const onContentsChanged = (opts: IContentsChangeOptions) => { this._onContentsChanged.fire(); - this._contentHoverWidget.onContentsChanged(); + this._contentHoverWidget.onContentsChanged(opts); }; const setMinimumDimensions = (dimensions: dom.Dimension) => { this._contentHoverWidget.setMinimumDimensions(dimensions); diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index ad5442f1fa2..babdf0f5556 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -95,11 +95,15 @@ export interface IEditorHoverColorPickerWidget { layout(): void; } +export interface IContentsChangeOptions { + allowPositionPreferenceRecomputation: boolean; +} + export interface IEditorHoverContext { /** * The contents rendered inside the fragment have been changed, which means that the hover should relayout. */ - onContentsChanged(): void; + onContentsChanged(opts?: IContentsChangeOptions): void; /** * Set the minimum dimensions of the resizable hover */ diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index 3fd5876cfe4..e98b45195e2 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -15,7 +15,7 @@ import { Position } from '../../../common/core/position.js'; import { Range } from '../../../common/core/range.js'; import { IModelDecoration, ITextModel } from '../../../common/model.js'; import { ILanguageService } from '../../../common/languages/language.js'; -import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from './hoverTypes.js'; +import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IContentsChangeOptions, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from './hoverTypes.js'; import * as nls from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; @@ -245,9 +245,9 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { private readonly _keybindingService: IKeybindingService, private readonly _hoverService: IHoverService, private readonly _configurationService: IConfigurationService, - private readonly _onFinishedRendering: () => void, + private readonly onContentsChanged: (opts?: IContentsChangeOptions | undefined) => void, ) { - this.renderedHoverParts = this._renderHoverParts(hoverParts, hoverPartsContainer, this._onFinishedRendering); + this.renderedHoverParts = this._renderHoverParts(hoverParts, hoverPartsContainer, this.onContentsChanged); this._disposables.add(toDisposable(() => { this.renderedHoverParts.forEach(renderedHoverPart => { renderedHoverPart.dispose(); @@ -416,7 +416,7 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { if (index >= this.renderedHoverParts.length || index < 0) { return undefined; } - const renderedHoverPart = this._renderHoverPart(hoverPart, this._onFinishedRendering); + const renderedHoverPart = this._renderHoverPart(hoverPart, () => this.onContentsChanged({ allowPositionPreferenceRecomputation: false })); const currentRenderedHoverPart = this.renderedHoverParts[index]; const currentRenderedMarkdown = currentRenderedHoverPart.hoverElement; const renderedMarkdown = renderedHoverPart.hoverElement; From 492692eed34e610999cb5d481b137fc035e68dbe Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 12 Dec 2024 10:44:59 +0100 Subject: [PATCH 132/479] Refactor chat setup context keys for consistency and clarity (#235909) --- .../contrib/chat/browser/chatSetup.ts | 4 +-- .../contrib/chat/browser/chatViewPane.ts | 25 +++---------------- .../contrib/chat/common/chatContextKeys.ts | 21 +++++++++++++++- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index ba55f935c5e..37544c7ec93 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -52,7 +52,7 @@ import { IChatAgentService } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { CHAT_CATEGORY } from './actions/chatActions.js'; import { ChatViewId, EditsViewId, ensureSideBarChatViewSize, IChatWidget, showChatView, showEditsView } from './chat.js'; -import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID, SetupWelcomeViewCondition } from './chatViewPane.js'; +import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID } from './chatViewPane.js'; import { ChatViewsWelcomeExtensions, IChatViewsWelcomeContributionRegistry } from './viewsWelcome/chatViewsWelcome.js'; import { IChatQuotasService } from './chatQuotasService.js'; import { mainWindow } from '../../../../base/browser/window.js'; @@ -122,7 +122,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr private registerChatWelcome(): void { Registry.as(ChatViewsWelcomeExtensions.ChatViewsWelcomeRegistry).register({ title: localize('welcomeChat', "Welcome to Copilot"), - when: SetupWelcomeViewCondition, + when: ChatContextKeys.SetupViewCondition, icon: Codicon.copilotLarge, content: disposables => disposables.add(this.instantiationService.createInstance(ChatSetupWelcomeContent, this.controller.value, this.context)).element, }); diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index 8e0e8bd85ff..742ae8cbf6b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -8,7 +8,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -104,7 +104,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { })); this._register(this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(SetupWelcomeViewKeys)) { + if (e.affectsSome(ChatContextKeys.SetupViewKeys)) { this._onDidChangeViewWelcomeState.fire(); } })); @@ -139,7 +139,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { } override shouldShowWelcome(): boolean { - const showSetup = this.contextKeyService.contextMatchesRules(SetupWelcomeViewCondition); + const showSetup = this.contextKeyService.contextMatchesRules(ChatContextKeys.SetupViewCondition); const noPersistedSessions = !this.chatService.hasSessions(); const shouldShow = this.didUnregisterProvider || !this._widget?.viewModel && noPersistedSessions || this.defaultParticipantRegistrationFailed || showSetup; this.logService.trace(`ChatViewPane#shouldShowWelcome(${this.chatOptions.location}) = ${shouldShow}: didUnregister=${this.didUnregisterProvider} || noViewModel:${!this._widget?.viewModel} && noPersistedSessions=${noPersistedSessions} || defaultParticipantRegistrationFailed=${this.defaultParticipantRegistrationFailed} || showSetup=${showSetup}`); @@ -279,22 +279,3 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { } } } - -export const SetupWelcomeViewKeys = new Set([ChatContextKeys.Setup.triggered.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Setup.signedOut.key, ChatContextKeys.Setup.canSignUp.key]); -export const SetupWelcomeViewCondition = ContextKeyExpr.and( - ContextKeyExpr.has('config.chat.experimental.offerSetup'), - ContextKeyExpr.or( - ContextKeyExpr.and( - ChatContextKeys.Setup.triggered, - ChatContextKeys.Setup.installed.negate() - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.canSignUp, - ChatContextKeys.Setup.installed - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.signedOut, - ChatContextKeys.Setup.installed - ) - ) -)!; diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index de13af60b8b..047a5824af2 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from '../../../../nls.js'; -import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { ChatAgentLocation } from './chatAgents.js'; export namespace ChatContextKeys { @@ -51,6 +51,25 @@ export namespace ChatContextKeys { installed: new RawContextKey('chatSetupInstalled', false, true), // True when the chat extension is installed. }; + export const SetupViewKeys = new Set([ChatContextKeys.Setup.triggered.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Setup.signedOut.key, ChatContextKeys.Setup.canSignUp.key]); + export const SetupViewCondition = ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.experimental.offerSetup'), + ContextKeyExpr.or( + ContextKeyExpr.and( + ChatContextKeys.Setup.triggered, + ChatContextKeys.Setup.installed.negate() + ), + ContextKeyExpr.and( + ChatContextKeys.Setup.canSignUp, + ChatContextKeys.Setup.installed + ), + ContextKeyExpr.and( + ChatContextKeys.Setup.signedOut, + ChatContextKeys.Setup.installed + ) + ) + )!; + export const chatQuotaExceeded = new RawContextKey('chatQuotaExceeded', false, true); export const completionsQuotaExceeded = new RawContextKey('completionsQuotaExceeded', false, true); } From 39b0575e393f0897d28574df0fa0da23ef3006be Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Thu, 12 Dec 2024 10:46:25 +0100 Subject: [PATCH 133/479] Mark bun.lock as jsonc Bun added support for a new lockfile format using the jsonc language. It uses an unconventional file extension, but it would be nice if VSCode understands it by default anyway. See https://github.com/oven-sh/bun/pull/15705 --- extensions/json/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/json/package.json b/extensions/json/package.json index 8844345d672..63ee0078c6b 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -62,6 +62,7 @@ ], "filenames": [ "babel.config.json", + "bun.lock", ".babelrc.json", ".ember-cli", "typedoc.json" From 92fbd7a2288ea3d14faded3d765b7094cc30c3b0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 12 Dec 2024 10:50:44 +0100 Subject: [PATCH 134/479] fix #114994 (#235910) --- .../workbench/browser/parts/views/viewPaneContainer.ts | 9 ++++++++- src/vs/workbench/browser/parts/views/viewsViewlet.ts | 6 ++++-- src/vs/workbench/contrib/debug/browser/debugViewlet.ts | 4 +++- .../contrib/extensions/browser/extensionsViewlet.ts | 5 +++-- .../workbench/contrib/files/browser/explorerViewlet.ts | 6 ++++-- src/vs/workbench/contrib/remote/browser/remote.ts | 5 +++-- .../contrib/scm/browser/scmViewPaneContainer.ts | 6 ++++-- .../contrib/testing/browser/testingViewPaneContainer.ts | 4 +++- 8 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 1ca314af9d2..bdac0037f3f 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -41,6 +41,7 @@ import { FocusedViewContext } from '../../../common/contextkeys.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { isHorizontal, IWorkbenchLayoutService, LayoutSettings } from '../../../services/layout/browser/layoutService.js'; import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; export const ViewsSubMenu = new MenuId('Views'); MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { @@ -380,6 +381,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { @IStorageService protected storageService: IStorageService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, @IViewDescriptorService protected viewDescriptorService: IViewDescriptorService, + @ILogService protected readonly logService: ILogService, ) { super(id, themeService, storageService); @@ -793,7 +795,12 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { singleViewPaneContainerTitle: viewDescriptor.singleViewPaneContainerTitle, }); - pane.render(); + try { + pane.render(); + } catch (error) { + this.logService.error(`Fail to render view ${viewDescriptor.id}`, error); + continue; + } const contextMenuDisposable = addDisposableListener(pane.draggableElement, 'contextmenu', e => { e.stopPropagation(); e.preventDefault(); diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index b08338ccac6..6afc8eb55d8 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -17,6 +17,7 @@ import { Event } from '../../../../base/common/event.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; export interface IViewletViewOptions extends IViewPaneOptions { readonly fromExtensionId?: ExtensionIdentifier; @@ -39,10 +40,11 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer { @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, @IWorkspaceContextService contextService: IWorkspaceContextService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService, ) { - super(viewletId, { mergeViewWithContainerWhenSingleView: false }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + super(viewletId, { mergeViewWithContainerWhenSingleView: false }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService); this._register(onDidChangeFilterValue(newFilterValue => { this.filterValue = newFilterValue; this.onFilterChanged(newFilterValue); diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index 6db69ee2dfb..10b8d244b2e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -35,6 +35,7 @@ import { IExtensionService } from '../../../services/extensions/common/extension import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; export class DebugViewPaneContainer extends ViewPaneContainer { @@ -60,8 +61,9 @@ export class DebugViewPaneContainer extends ViewPaneContainer { @IContextViewService private readonly contextViewService: IContextViewService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService, ) { - super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService); // When there are potential updates to the docked debug toolbar we need to update it this._register(this.debugService.onDidChangeState(state => this.onDebugServiceStateChange(state))); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 5fafa931213..7ae9f77168d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -525,9 +525,10 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE @IExtensionService extensionService: IExtensionService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @ICommandService private readonly commandService: ICommandService + @ICommandService private readonly commandService: ICommandService, + @ILogService logService: ILogService, ) { - super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService); this.searchDelayer = new Delayer(500); this.extensionsSearchValueContextKey = ExtensionsSearchValueContext.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 18b860dbafe..48d50144a3b 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -38,6 +38,7 @@ import { isMacintosh, isWeb } from '../../../../base/common/platform.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { isMouseEvent } from '../../../../base/browser/dom.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; const explorerViewIcon = registerIcon('explorer-view-icon', Codicon.files, localize('explorerViewIcon', 'View icon of the explorer view.')); const openEditorsViewIcon = registerIcon('open-editors-view-icon', Codicon.book, localize('openEditorsIcon', 'View icon of the open editors view.')); @@ -167,10 +168,11 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { @IThemeService themeService: IThemeService, @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService, ) { - super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService); this.viewletVisibleContextKey = ExplorerViewletVisibleContext.bindTo(contextKeyService); this._register(this.contextService.onDidChangeWorkspaceName(e => this.updateTitleArea())); diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 8fef141d4e7..d2d6835f722 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -540,9 +540,10 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService, ) { - super(VIEWLET_ID, remoteExplorerService.onDidChangeTargetType, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, viewDescriptorService); + super(VIEWLET_ID, remoteExplorerService.onDidChangeTargetType, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, viewDescriptorService, logService); this.addConstantViewDescriptors([this.helpPanelDescriptor]); this._register(this.remoteSwitcher = this.instantiationService.createInstance(SwitchRemoteViewItem)); this.remoteExplorerService.onDidChangeHelpInformation(extensions => { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts b/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts index 6f6fccf828b..5f813a68b26 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPaneContainer.ts @@ -17,6 +17,7 @@ import { IExtensionService } from '../../../services/extensions/common/extension import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IViewDescriptorService } from '../../../common/views.js'; import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; export class SCMViewPaneContainer extends ViewPaneContainer { @@ -30,9 +31,10 @@ export class SCMViewPaneContainer extends ViewPaneContainer { @IConfigurationService configurationService: IConfigurationService, @IExtensionService extensionService: IExtensionService, @IWorkspaceContextService contextService: IWorkspaceContextService, - @IViewDescriptorService viewDescriptorService: IViewDescriptorService + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService, ) { - super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + super(VIEWLET_ID, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService); } override create(parent: HTMLElement): void { diff --git a/src/vs/workbench/contrib/testing/browser/testingViewPaneContainer.ts b/src/vs/workbench/contrib/testing/browser/testingViewPaneContainer.ts index 60f217ad9e4..4c179a5669b 100644 --- a/src/vs/workbench/contrib/testing/browser/testingViewPaneContainer.ts +++ b/src/vs/workbench/contrib/testing/browser/testingViewPaneContainer.ts @@ -16,6 +16,7 @@ import { IViewDescriptorService } from '../../../common/views.js'; import { Testing } from '../common/constants.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; export class TestingViewPaneContainer extends ViewPaneContainer { @@ -30,8 +31,9 @@ export class TestingViewPaneContainer extends ViewPaneContainer { @IExtensionService extensionService: IExtensionService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, + @ILogService logService: ILogService, ) { - super(Testing.ViewletId, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); + super(Testing.ViewletId, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService, logService); } override create(parent: HTMLElement): void { From 494787526e40d1b8f6782ff9975ed64c43499928 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Thu, 12 Dec 2024 10:54:34 +0100 Subject: [PATCH 135/479] Allow the .ndjson extension for the jsonl language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NDJSON is a specification that is essentially the same thing as JSON lines. The spec can be found on https://github.com/ndjson/ndjson-spec. The NDJSON website is down. The project seems pretty much dead. The status is best described by this comment: https://github.com/ndjson/ndjson-spec/issues/35#issuecomment-1285673417 However, this doesn’t mean there aren’t any `.ndjson` files out there. This changes adds `.ndjson` to the list of `jsonl` file extensions. --- extensions/json/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/json/package.json b/extensions/json/package.json index 8844345d672..6bc5311740e 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -74,7 +74,8 @@ "JSON Lines" ], "extensions": [ - ".jsonl" + ".jsonl", + ".ndjson" ], "filenames": [], "configuration": "./language-configuration.json" From 87d4b889cc311e95e7bef84080fdca96e24b89d6 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 12 Dec 2024 10:54:53 +0100 Subject: [PATCH 136/479] [Bug] Certain inline codeblocks are not displayed in release notes (#235911) Fixes #235886 --- src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index e31ef89c55b..d8c9fbd63bb 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -288,10 +288,6 @@ export class ReleaseNotesManager { /* codesetting */ - code:has(.codesetting)+code:not(:has(.codesetting)) { - display: none; - } - code:has(.codesetting) { background-color: var(--vscode-textPreformat-background); color: var(--vscode-textPreformat-foreground); From 0b7b0c3ca5c917f08e858f1a620df7c9b2de02fe Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 12 Dec 2024 11:02:57 +0100 Subject: [PATCH 137/479] Fix `setting()` showing in release notes for non-existant settings (#235914) --- .../contrib/markdown/browser/markdownSettingRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts index c2d223869d9..14dfabbd451 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts @@ -121,7 +121,7 @@ export class SimpleSettingRenderer { private render(settingId: string, newValue: string): string | undefined { const setting = this.getSetting(settingId); if (!setting) { - return ''; + return `${settingId}`; } return this.renderSetting(setting, newValue); From fda1af2e8fe691a2a222d1d0100c91bfeb6b8027 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 12 Dec 2024 11:28:53 +0100 Subject: [PATCH 138/479] [theme] avoid flashing when starting up with a new OS color scheme (#235913) * avoid flickering when autoDetectColorScheme is on * update --- src/vs/platform/theme/common/theme.ts | 8 + src/vs/platform/theme/common/themeService.ts | 14 +- .../theme/electron-main/themeMainService.ts | 58 +++++--- .../themes/browser/themes.contribution.ts | 137 +----------------- .../themes/browser/workbenchThemeService.ts | 29 ++-- .../services/themes/common/colorThemeData.ts | 16 +- .../themes/common/themeExtensionPoints.ts | 7 +- .../themes/common/workbenchThemeService.ts | 10 +- .../nativeHostColorSchemeService.ts | 20 +-- 9 files changed, 80 insertions(+), 219 deletions(-) diff --git a/src/vs/platform/theme/common/theme.ts b/src/vs/platform/theme/common/theme.ts index 856b2f12eb5..0f298890998 100644 --- a/src/vs/platform/theme/common/theme.ts +++ b/src/vs/platform/theme/common/theme.ts @@ -13,6 +13,14 @@ export enum ColorScheme { HIGH_CONTRAST_LIGHT = 'hcLight' } +export enum ThemeTypeSelector { + VS = 'vs', + VS_DARK = 'vs-dark', + HC_BLACK = 'hc-black', + HC_LIGHT = 'hc-light' +} + + export function isHighContrast(scheme: ColorScheme): boolean { return scheme === ColorScheme.HIGH_CONTRAST_DARK || scheme === ColorScheme.HIGH_CONTRAST_LIGHT; } diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index cf850e7726e..bca5b3b3b3e 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -12,7 +12,7 @@ import { createDecorator } from '../../instantiation/common/instantiation.js'; import * as platform from '../../registry/common/platform.js'; import { ColorIdentifier } from './colorRegistry.js'; import { IconContribution, IconDefinition } from './iconRegistry.js'; -import { ColorScheme } from './theme.js'; +import { ColorScheme, ThemeTypeSelector } from './theme.js'; export const IThemeService = createDecorator('themeService'); @@ -23,12 +23,12 @@ export function themeColorFromId(id: ColorIdentifier) { export const FileThemeIcon = Codicon.file; export const FolderThemeIcon = Codicon.folder; -export function getThemeTypeSelector(type: ColorScheme): string { +export function getThemeTypeSelector(type: ColorScheme): ThemeTypeSelector { switch (type) { - case ColorScheme.DARK: return 'vs-dark'; - case ColorScheme.HIGH_CONTRAST_DARK: return 'hc-black'; - case ColorScheme.HIGH_CONTRAST_LIGHT: return 'hc-light'; - default: return 'vs'; + case ColorScheme.DARK: return ThemeTypeSelector.VS_DARK; + case ColorScheme.HIGH_CONTRAST_DARK: return ThemeTypeSelector.HC_BLACK; + case ColorScheme.HIGH_CONTRAST_LIGHT: return ThemeTypeSelector.HC_LIGHT; + default: return ThemeTypeSelector.VS; } } @@ -208,7 +208,7 @@ export class Themable extends Disposable { export interface IPartsSplash { zoomLevel: number | undefined; - baseTheme: string; + baseTheme: ThemeTypeSelector; colorInfo: { background: string; foreground: string | undefined; diff --git a/src/vs/platform/theme/electron-main/themeMainService.ts b/src/vs/platform/theme/electron-main/themeMainService.ts index aea7ebbb82b..3cc4fb246e1 100644 --- a/src/vs/platform/theme/electron-main/themeMainService.ts +++ b/src/vs/platform/theme/electron-main/themeMainService.ts @@ -12,6 +12,7 @@ import { createDecorator } from '../../instantiation/common/instantiation.js'; import { IStateService } from '../../state/node/state.js'; import { IPartsSplash } from '../common/themeService.js'; import { IColorScheme } from '../../window/common/window.js'; +import { ThemeTypeSelector } from '../common/theme.js'; // These default colors match our default themes // editor background color ("Dark Modern", etc...) @@ -26,6 +27,7 @@ const THEME_WINDOW_SPLASH = 'windowSplash'; namespace ThemeSettings { export const DETECT_COLOR_SCHEME = 'window.autoDetectColorScheme'; + export const DETECT_HC = 'window.autoDetectHighContrast'; export const SYSTEM_COLOR_THEME = 'window.systemColorTheme'; } @@ -82,9 +84,9 @@ export class ThemeMainService extends Disposable implements IThemeMainService { electron.nativeTheme.themeSource = 'light'; break; case 'auto': - switch (this.getBaseTheme()) { - case 'vs': electron.nativeTheme.themeSource = 'light'; break; - case 'vs-dark': electron.nativeTheme.themeSource = 'dark'; break; + switch (this.getPreferredBaseTheme() ?? this.getStoredBaseTheme()) { + case ThemeTypeSelector.VS: electron.nativeTheme.themeSource = 'light'; break; + case ThemeTypeSelector.VS_DARK: electron.nativeTheme.themeSource = 'dark'; break; default: electron.nativeTheme.themeSource = 'system'; } break; @@ -98,7 +100,7 @@ export class ThemeMainService extends Disposable implements IThemeMainService { getColorScheme(): IColorScheme { if (isWindows) { - // high contrast is refelected by the shouldUseInvertedColorScheme property + // high contrast is reflected by the shouldUseInvertedColorScheme property if (electron.nativeTheme.shouldUseHighContrastColors) { // shouldUseInvertedColorScheme is dark, !shouldUseInvertedColorScheme is light return { dark: electron.nativeTheme.shouldUseInvertedColorScheme, highContrast: true }; @@ -120,32 +122,44 @@ export class ThemeMainService extends Disposable implements IThemeMainService { }; } - getBackgroundColor(): string { + getPreferredBaseTheme(): ThemeTypeSelector | undefined { const colorScheme = this.getColorScheme(); - if (colorScheme.highContrast && this.configurationService.getValue('window.autoDetectHighContrast')) { - return colorScheme.dark ? DEFAULT_BG_HC_BLACK : DEFAULT_BG_HC_LIGHT; + if (this.configurationService.getValue(ThemeSettings.DETECT_HC) && colorScheme.highContrast) { + return colorScheme.dark ? ThemeTypeSelector.HC_BLACK : ThemeTypeSelector.HC_LIGHT; + } + if (this.configurationService.getValue(ThemeSettings.DETECT_COLOR_SCHEME)) { + return colorScheme.dark ? ThemeTypeSelector.VS_DARK : ThemeTypeSelector.VS; } + return undefined; + } - let background = this.stateService.getItem(THEME_BG_STORAGE_KEY, null); - if (!background) { - switch (this.getBaseTheme()) { - case 'vs': background = DEFAULT_BG_LIGHT; break; - case 'hc-black': background = DEFAULT_BG_HC_BLACK; break; - case 'hc-light': background = DEFAULT_BG_HC_LIGHT; break; - default: background = DEFAULT_BG_DARK; + getBackgroundColor(): string { + const preferred = this.getPreferredBaseTheme(); + const stored = this.getStoredBaseTheme(); + + // If the stored theme has the same base as the preferred, we can return the stored background + if (preferred === undefined || preferred === stored) { + const storedBackground = this.stateService.getItem(THEME_BG_STORAGE_KEY, null); + if (storedBackground) { + return storedBackground; } } - - return background; + // Otherwise we return the default background for the preferred base theme. If there's no preferred, use the stored one. + switch (preferred ?? stored) { + case ThemeTypeSelector.VS: return DEFAULT_BG_LIGHT; + case ThemeTypeSelector.HC_BLACK: return DEFAULT_BG_HC_BLACK; + case ThemeTypeSelector.HC_LIGHT: return DEFAULT_BG_HC_LIGHT; + default: return DEFAULT_BG_DARK; + } } - private getBaseTheme(): 'vs' | 'vs-dark' | 'hc-black' | 'hc-light' { - const baseTheme = this.stateService.getItem(THEME_STORAGE_KEY, 'vs-dark').split(' ')[0]; + private getStoredBaseTheme(): ThemeTypeSelector { + const baseTheme = this.stateService.getItem(THEME_STORAGE_KEY, ThemeTypeSelector.VS_DARK).split(' ')[0]; switch (baseTheme) { - case 'vs': return 'vs'; - case 'hc-black': return 'hc-black'; - case 'hc-light': return 'hc-light'; - default: return 'vs-dark'; + case ThemeTypeSelector.VS: return ThemeTypeSelector.VS; + case ThemeTypeSelector.HC_BLACK: return ThemeTypeSelector.HC_BLACK; + case ThemeTypeSelector.HC_LIGHT: return ThemeTypeSelector.HC_LIGHT; + default: return ThemeTypeSelector.VS_DARK; } } diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 8549b93b80c..e8e7b606c86 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -9,7 +9,7 @@ import { MenuRegistry, MenuId, Action2, registerAction2, ISubmenuItem } from '.. import { equalsIgnoreCase } from '../../../../base/common/strings.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { IWorkbenchThemeService, IWorkbenchTheme, ThemeSettingTarget, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme, ThemeSettings, ThemeSettingDefaults } from '../../../services/themes/common/workbenchThemeService.js'; +import { IWorkbenchThemeService, IWorkbenchTheme, ThemeSettingTarget, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme, ThemeSettings } from '../../../services/themes/common/workbenchThemeService.js'; import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; import { IExtensionGalleryService, IExtensionManagementService, IGalleryExtension } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from '../../../../platform/theme/common/colorRegistry.js'; @@ -31,17 +31,12 @@ import { Emitter } from '../../../../base/common/event.js'; import { IExtensionResourceLoaderService } from '../../../../platform/extensionResourceLoader/common/extensionResourceLoader.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js'; +import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { FileIconThemeData } from '../../../services/themes/browser/fileIconThemeData.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from '../../../common/contributions.js'; -import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; -import { INotificationService, IPromptChoice, Severity } from '../../../../platform/notification/common/notification.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { isWeb } from '../../../../base/common/platform.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { IHostService } from '../../../services/host/browser/host.js'; +import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; + import { mainWindow } from '../../../../base/browser/window.js'; import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; import { Toggle } from '../../../../base/browser/ui/toggle/toggle.js'; @@ -843,127 +838,3 @@ MenuRegistry.appendMenuItem(ThemesSubMenu, { }, order: 3 }); - -type DefaultThemeUpdatedNotificationReaction = 'keepNew' | 'keepOld' | 'tryNew' | 'cancel' | 'browse'; - -class DefaultThemeUpdatedNotificationContribution implements IWorkbenchContribution { - - static STORAGE_KEY = 'themeUpdatedNotificationShown'; - - constructor( - @INotificationService private readonly _notificationService: INotificationService, - @IWorkbenchThemeService private readonly _workbenchThemeService: IWorkbenchThemeService, - @IStorageService private readonly _storageService: IStorageService, - @ICommandService private readonly _commandService: ICommandService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IHostService private readonly _hostService: IHostService, - ) { - if (_storageService.getBoolean(DefaultThemeUpdatedNotificationContribution.STORAGE_KEY, StorageScope.APPLICATION)) { - return; - } - setTimeout(async () => { - if (_storageService.getBoolean(DefaultThemeUpdatedNotificationContribution.STORAGE_KEY, StorageScope.APPLICATION)) { - return; - } - if (await this._hostService.hadLastFocus()) { - this._storageService.store(DefaultThemeUpdatedNotificationContribution.STORAGE_KEY, true, StorageScope.APPLICATION, StorageTarget.USER); - if (this._workbenchThemeService.hasUpdatedDefaultThemes()) { - this._showYouGotMigratedNotification(); - } else { - const currentTheme = this._workbenchThemeService.getColorTheme().settingsId; - if (currentTheme === ThemeSettingDefaults.COLOR_THEME_LIGHT_OLD || currentTheme === ThemeSettingDefaults.COLOR_THEME_DARK_OLD) { - this._tryNewThemeNotification(); - } - } - } - }, 3000); - } - - private async _showYouGotMigratedNotification(): Promise { - const usingLight = this._workbenchThemeService.getColorTheme().type === ColorScheme.LIGHT; - const newThemeSettingsId = usingLight ? ThemeSettingDefaults.COLOR_THEME_LIGHT : ThemeSettingDefaults.COLOR_THEME_DARK; - const newTheme = (await this._workbenchThemeService.getColorThemes()).find(theme => theme.settingsId === newThemeSettingsId); - if (newTheme) { - const choices = [ - { - label: localize('button.keep', "Keep New Theme"), - run: () => { - this._writeTelemetry('keepNew'); - } - }, - { - label: localize('button.browse', "Browse Themes"), - run: () => { - this._writeTelemetry('browse'); - this._commandService.executeCommand(SelectColorThemeCommandId); - } - }, - { - label: localize('button.revert', "Revert"), - run: async () => { - this._writeTelemetry('keepOld'); - const oldSettingsId = usingLight ? ThemeSettingDefaults.COLOR_THEME_LIGHT_OLD : ThemeSettingDefaults.COLOR_THEME_DARK_OLD; - const oldTheme = (await this._workbenchThemeService.getColorThemes()).find(theme => theme.settingsId === oldSettingsId); - if (oldTheme) { - this._workbenchThemeService.setColorTheme(oldTheme, 'auto'); - } - } - } - ]; - await this._notificationService.prompt( - Severity.Info, - localize({ key: 'themeUpdatedNotification', comment: ['{0} is the name of the new default theme'] }, "Visual Studio Code now ships with a new default theme '{0}'. If you prefer, you can switch back to the old theme or try one of the many other color themes available.", newTheme.label), - choices, - { - onCancel: () => this._writeTelemetry('cancel') - } - ); - } - } - - private async _tryNewThemeNotification(): Promise { - const newThemeSettingsId = this._workbenchThemeService.getColorTheme().type === ColorScheme.LIGHT ? ThemeSettingDefaults.COLOR_THEME_LIGHT : ThemeSettingDefaults.COLOR_THEME_DARK; - const theme = (await this._workbenchThemeService.getColorThemes()).find(theme => theme.settingsId === newThemeSettingsId); - if (theme) { - const choices: IPromptChoice[] = [{ - label: localize('button.tryTheme', "Try New Theme"), - run: () => { - this._writeTelemetry('tryNew'); - this._workbenchThemeService.setColorTheme(theme, 'auto'); - } - }, - { - label: localize('button.cancel', "Cancel"), - run: () => { - this._writeTelemetry('cancel'); - } - }]; - await this._notificationService.prompt( - Severity.Info, - localize({ key: 'newThemeNotification', comment: ['{0} is the name of the new default theme'] }, "Visual Studio Code now ships with a new default theme '{0}'. Do you want to give it a try?", theme.label), - choices, - { onCancel: () => this._writeTelemetry('cancel') } - ); - } - } - - private _writeTelemetry(outcome: DefaultThemeUpdatedNotificationReaction): void { - type ThemeUpdatedNoticationClassification = { - owner: 'aeschli'; - comment: 'Reaction to the notification that theme has updated to a new default theme'; - web: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this is running on web' }; - reaction: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Outcome of the notification' }; - }; - type ThemeUpdatedNoticationEvent = { - web: boolean; - reaction: DefaultThemeUpdatedNotificationReaction; - }; - - this._telemetryService.publicLog2('themeUpdatedNotication', { - web: isWeb, - reaction: outcome - }); - } -} -const workbenchRegistry = Registry.as(Extensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(DefaultThemeUpdatedNotificationContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index a74a0b263c2..baf78a990da 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -6,7 +6,7 @@ import * as nls from '../../../../nls.js'; import * as types from '../../../../base/common/types.js'; import { IExtensionService } from '../../extensions/common/extensions.js'; -import { IWorkbenchThemeService, IWorkbenchColorTheme, IWorkbenchFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, VS_HC_LIGHT_THEME, ThemeSettings, IWorkbenchProductIconTheme, ThemeSettingTarget, ThemeSettingDefaults, COLOR_THEME_DARK_INITIAL_COLORS, COLOR_THEME_LIGHT_INITIAL_COLORS } from '../common/workbenchThemeService.js'; +import { IWorkbenchThemeService, IWorkbenchColorTheme, IWorkbenchFileIconTheme, ExtensionData, ThemeSettings, IWorkbenchProductIconTheme, ThemeSettingTarget, ThemeSettingDefaults, COLOR_THEME_DARK_INITIAL_COLORS, COLOR_THEME_LIGHT_INITIAL_COLORS } from '../common/workbenchThemeService.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; @@ -34,7 +34,7 @@ import { ProductIconThemeData, DEFAULT_PRODUCT_ICON_THEME_ID } from './productIc import { registerProductIconThemeSchemas } from '../common/productIconThemeSchema.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { isWeb } from '../../../../base/common/platform.js'; -import { ColorScheme } from '../../../../platform/theme/common/theme.js'; +import { ColorScheme, ThemeTypeSelector } from '../../../../platform/theme/common/theme.js'; import { IHostColorSchemeService } from '../common/hostColorSchemeService.js'; import { RunOnceScheduler, Sequencer } from '../../../../base/common/async.js'; import { IUserDataInitializationService } from '../../userData/browser/userDataInit.js'; @@ -59,10 +59,10 @@ const themingRegistry = Registry.as(ThemingExtensions.ThemingC function validateThemeId(theme: string): string { // migrations switch (theme) { - case VS_LIGHT_THEME: return `vs ${defaultThemeExtensionId}-themes-light_vs-json`; - case VS_DARK_THEME: return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`; - case VS_HC_THEME: return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`; - case VS_HC_LIGHT_THEME: return `hc-light ${defaultThemeExtensionId}-themes-hc_light-json`; + case ThemeTypeSelector.VS: return `vs ${defaultThemeExtensionId}-themes-light_vs-json`; + case ThemeTypeSelector.VS_DARK: return `vs-dark ${defaultThemeExtensionId}-themes-dark_vs-json`; + case ThemeTypeSelector.HC_BLACK: return `hc-black ${defaultThemeExtensionId}-themes-hc_black-json`; + case ThemeTypeSelector.HC_LIGHT: return `hc-light ${defaultThemeExtensionId}-themes-hc_light-json`; } return theme; } @@ -97,8 +97,6 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme private readonly productIconThemeWatcher: ThemeFileWatcher; private readonly productIconThemeSequencer: Sequencer; - private hasDefaultUpdated: boolean = false; - constructor( @IExtensionService extensionService: IExtensionService, @IStorageService private readonly storageService: IStorageService, @@ -143,15 +141,11 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme // a color theme document with good defaults until the theme is loaded let themeData: ColorThemeData | undefined = ColorThemeData.fromStorageData(this.storageService); const colorThemeSetting = this.settings.colorTheme; - if (themeData && colorThemeSetting !== themeData.settingsId && this.settings.isDefaultColorTheme()) { - this.hasDefaultUpdated = themeData.settingsId === ThemeSettingDefaults.COLOR_THEME_DARK_OLD || themeData.settingsId === ThemeSettingDefaults.COLOR_THEME_LIGHT_OLD; - - // the web has different defaults than the desktop, therefore do not restore when the setting is the default theme and the storage doesn't match that. + if (themeData && colorThemeSetting !== themeData.settingsId) { themeData = undefined; } const defaultColorMap = colorThemeSetting === ThemeSettingDefaults.COLOR_THEME_LIGHT ? COLOR_THEME_LIGHT_INITIAL_COLORS : colorThemeSetting === ThemeSettingDefaults.COLOR_THEME_DARK ? COLOR_THEME_DARK_INITIAL_COLORS : undefined; - if (!themeData) { const initialColorTheme = environmentService.options?.initialColorTheme; if (initialColorTheme) { @@ -159,7 +153,8 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme } } if (!themeData) { - themeData = ColorThemeData.createUnloadedThemeForThemeType(isWeb ? ColorScheme.LIGHT : ColorScheme.DARK, defaultColorMap); + const colorScheme = this.settings.getPreferredColorScheme() ?? (isWeb ? ColorScheme.LIGHT : ColorScheme.DARK); + themeData = ColorThemeData.createUnloadedThemeForThemeType(colorScheme, defaultColorMap); } themeData.setCustomizations(this.settings); this.applyTheme(themeData, undefined, true); @@ -367,10 +362,6 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme })); } - public hasUpdatedDefaultThemes(): boolean { - return this.hasDefaultUpdated; - } - public getColorTheme(): IWorkbenchColorTheme { return this.currentColorTheme; } @@ -497,7 +488,7 @@ export class WorkbenchThemeService extends Disposable implements IWorkbenchTheme if (this.currentColorTheme.id) { this.container.classList.remove(...this.currentColorTheme.classNames); } else { - this.container.classList.remove(VS_DARK_THEME, VS_LIGHT_THEME, VS_HC_THEME, VS_HC_LIGHT_THEME); + this.container.classList.remove(ThemeTypeSelector.VS, ThemeTypeSelector.VS_DARK, ThemeTypeSelector.HC_BLACK, ThemeTypeSelector.HC_LIGHT); } this.container.classList.add(...newTheme.classNames); diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index d6674cb5ab0..c0e9de4ef17 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -6,7 +6,7 @@ import { basename } from '../../../../base/common/path.js'; import * as Json from '../../../../base/common/json.js'; import { Color } from '../../../../base/common/color.js'; -import { ExtensionData, ITokenColorCustomizations, ITextMateThemingRule, IWorkbenchColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations, ISemanticTokenRules, ISemanticTokenColorizationSetting, ISemanticTokenColorCustomizations, IThemeScopableCustomizations, IThemeScopedCustomizations, THEME_SCOPE_CLOSE_PAREN, THEME_SCOPE_OPEN_PAREN, themeScopeRegex, THEME_SCOPE_WILDCARD, VS_HC_LIGHT_THEME } from './workbenchThemeService.js'; +import { ExtensionData, ITokenColorCustomizations, ITextMateThemingRule, IWorkbenchColorTheme, IColorMap, IThemeExtensionPoint, IColorCustomizations, ISemanticTokenRules, ISemanticTokenColorizationSetting, ISemanticTokenColorCustomizations, IThemeScopableCustomizations, IThemeScopedCustomizations, THEME_SCOPE_CLOSE_PAREN, THEME_SCOPE_OPEN_PAREN, themeScopeRegex, THEME_SCOPE_WILDCARD } from './workbenchThemeService.js'; import { convertSettings } from './themeCompatibility.js'; import * as nls from '../../../../nls.js'; import * as types from '../../../../base/common/types.js'; @@ -23,7 +23,7 @@ import { IExtensionResourceLoaderService } from '../../../../platform/extensionR import { CharCode } from '../../../../base/common/charCode.js'; import { StorageScope, IStorageService, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { ThemeConfiguration } from './themeConfiguration.js'; -import { ColorScheme } from '../../../../platform/theme/common/theme.js'; +import { ColorScheme, ThemeTypeSelector } from '../../../../platform/theme/common/theme.js'; import { ColorId, FontStyle, MetadataConsts } from '../../../../editor/common/encodedTokenAttributes.js'; import { toStandardTokenType } from '../../../../editor/common/languages/supports/tokenization.js'; @@ -584,8 +584,8 @@ export class ColorThemeData implements IWorkbenchColorTheme { storageService.store(ColorThemeData.STORAGE_KEY, value, StorageScope.PROFILE, StorageTarget.USER); } - get baseTheme(): string { - return this.classNames[0]; + get themeTypeSelector(): ThemeTypeSelector { + return this.classNames[0] as ThemeTypeSelector; } get classNames(): string[] { @@ -593,10 +593,10 @@ export class ColorThemeData implements IWorkbenchColorTheme { } get type(): ColorScheme { - switch (this.baseTheme) { - case VS_LIGHT_THEME: return ColorScheme.LIGHT; - case VS_HC_THEME: return ColorScheme.HIGH_CONTRAST_DARK; - case VS_HC_LIGHT_THEME: return ColorScheme.HIGH_CONTRAST_LIGHT; + switch (this.themeTypeSelector) { + case ThemeTypeSelector.VS: return ColorScheme.LIGHT; + case ThemeTypeSelector.HC_BLACK: return ColorScheme.HIGH_CONTRAST_DARK; + case ThemeTypeSelector.HC_LIGHT: return ColorScheme.HIGH_CONTRAST_LIGHT; default: return ColorScheme.DARK; } } diff --git a/src/vs/workbench/services/themes/common/themeExtensionPoints.ts b/src/vs/workbench/services/themes/common/themeExtensionPoints.ts index 8ab53a63902..028e88c2a29 100644 --- a/src/vs/workbench/services/themes/common/themeExtensionPoints.ts +++ b/src/vs/workbench/services/themes/common/themeExtensionPoints.ts @@ -8,7 +8,7 @@ import * as nls from '../../../../nls.js'; import * as types from '../../../../base/common/types.js'; import * as resources from '../../../../base/common/resources.js'; import { ExtensionMessageCollector, IExtensionPoint, ExtensionsRegistry } from '../../extensions/common/extensionsRegistry.js'; -import { ExtensionData, IThemeExtensionPoint, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, VS_HC_LIGHT_THEME } from './workbenchThemeService.js'; +import { ExtensionData, IThemeExtensionPoint } from './workbenchThemeService.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import { URI } from '../../../../base/common/uri.js'; @@ -18,6 +18,7 @@ import { IExtensionManifest } from '../../../../platform/extensions/common/exten import { IMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { ThemeTypeSelector } from '../../../../platform/theme/common/theme.js'; export function registerColorThemeExtensionPoint() { return ExtensionsRegistry.registerExtensionPoint({ @@ -27,7 +28,7 @@ export function registerColorThemeExtensionPoint() { type: 'array', items: { type: 'object', - defaultSnippets: [{ body: { label: '${1:label}', id: '${2:id}', uiTheme: VS_DARK_THEME, path: './themes/${3:id}.tmTheme.' } }], + defaultSnippets: [{ body: { label: '${1:label}', id: '${2:id}', uiTheme: ThemeTypeSelector.VS_DARK, path: './themes/${3:id}.tmTheme.' } }], properties: { id: { description: nls.localize('vscode.extension.contributes.themes.id', 'Id of the color theme as used in the user settings.'), @@ -39,7 +40,7 @@ export function registerColorThemeExtensionPoint() { }, uiTheme: { description: nls.localize('vscode.extension.contributes.themes.uiTheme', 'Base theme defining the colors around the editor: \'vs\' is the light color theme, \'vs-dark\' is the dark color theme. \'hc-black\' is the dark high contrast theme, \'hc-light\' is the light high contrast theme.'), - enum: [VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, VS_HC_LIGHT_THEME] + enum: [ThemeTypeSelector.VS, ThemeTypeSelector.VS_DARK, ThemeTypeSelector.HC_BLACK, ThemeTypeSelector.HC_LIGHT] }, path: { description: nls.localize('vscode.extension.contributes.themes.path', 'Path of the tmTheme file. The path is relative to the extension folder and is typically \'./colorthemes/awesome-color-theme.json\'.'), diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index ba0808026b4..3806f1a2d6c 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -10,15 +10,10 @@ import { IColorTheme, IThemeService, IFileIconTheme, IProductIconTheme } from '. import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; import { isBoolean, isString } from '../../../../base/common/types.js'; import { IconContribution, IconDefinition } from '../../../../platform/theme/common/iconRegistry.js'; -import { ColorScheme } from '../../../../platform/theme/common/theme.js'; +import { ColorScheme, ThemeTypeSelector } from '../../../../platform/theme/common/theme.js'; export const IWorkbenchThemeService = refineServiceDecorator(IThemeService); -export const VS_LIGHT_THEME = 'vs'; -export const VS_DARK_THEME = 'vs-dark'; -export const VS_HC_THEME = 'hc-black'; -export const VS_HC_LIGHT_THEME = 'hc-light'; - export const THEME_SCOPE_OPEN_PAREN = '['; export const THEME_SCOPE_CLOSE_PAREN = ']'; export const THEME_SCOPE_WILDCARD = '*'; @@ -147,7 +142,6 @@ export interface IWorkbenchThemeService extends IThemeService { getMarketplaceColorThemes(publisher: string, name: string, version: string): Promise; onDidColorThemeChange: Event; - hasUpdatedDefaultThemes(): boolean; getPreferredColorScheme(): ColorScheme | undefined; setFileIconTheme(iconThemeId: string | undefined | IWorkbenchFileIconTheme, settingsTarget: ThemeSettingTarget): Promise; @@ -281,6 +275,6 @@ export interface IThemeExtensionPoint { label?: string; description?: string; path: string; - uiTheme?: typeof VS_LIGHT_THEME | typeof VS_DARK_THEME | typeof VS_HC_THEME | typeof VS_HC_LIGHT_THEME; + uiTheme?: ThemeTypeSelector; _watch: boolean; // unsupported options to watch location } diff --git a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts index 2357d240fd7..efb620ad13f 100644 --- a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts +++ b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts @@ -9,8 +9,6 @@ import { InstantiationType, registerSingleton } from '../../../../platform/insta import { Disposable } from '../../../../base/common/lifecycle.js'; import { IHostColorSchemeService } from '../common/hostColorSchemeService.js'; import { INativeWorkbenchEnvironmentService } from '../../environment/electron-sandbox/environmentService.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { isBoolean, isObject } from '../../../../base/common/types.js'; import { IColorScheme } from '../../../../platform/window/common/window.js'; export class NativeHostColorSchemeService extends Disposable implements IHostColorSchemeService { @@ -28,14 +26,13 @@ export class NativeHostColorSchemeService extends Disposable implements IHostCol constructor( @INativeHostService private readonly nativeHostService: INativeHostService, @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, - @IStorageService private storageService: IStorageService ) { super(); // register listener with the OS this._register(this.nativeHostService.onDidChangeColorScheme(scheme => this.update(scheme))); - const initial = this.getStoredValue() ?? environmentService.window.colorScheme; + const initial = environmentService.window.colorScheme; this.dark = initial.dark; this.highContrast = initial.highContrast; @@ -43,27 +40,12 @@ export class NativeHostColorSchemeService extends Disposable implements IHostCol this.nativeHostService.getOSColorScheme().then(scheme => this.update(scheme)); } - private getStoredValue(): IColorScheme | undefined { - const stored = this.storageService.get(NativeHostColorSchemeService.STORAGE_KEY, StorageScope.APPLICATION); - if (stored) { - try { - const scheme = JSON.parse(stored); - if (isObject(scheme) && isBoolean(scheme.highContrast) && isBoolean(scheme.dark)) { - return scheme as IColorScheme; - } - } catch (e) { - // ignore - } - } - return undefined; - } private update({ highContrast, dark }: IColorScheme) { if (dark !== this.dark || highContrast !== this.highContrast) { this.dark = dark; this.highContrast = highContrast; - this.storageService.store(NativeHostColorSchemeService.STORAGE_KEY, JSON.stringify({ highContrast, dark }), StorageScope.APPLICATION, StorageTarget.MACHINE); this._onDidChangeColorScheme.fire(); } } From d590c7493372ec9913c3017555269ad1107017f7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 12 Dec 2024 11:46:56 +0100 Subject: [PATCH 139/479] don't spread huge arrays, use insertInto to avoid runtime failure (#235926) https://github.com/microsoft/vscode/issues/235889 --- src/vs/base/browser/ui/tree/abstractTree.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 64efdcaabea..38570ae882d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -20,7 +20,7 @@ import { IToggleStyles, Toggle, unthemedToggleStyles } from '../toggle/toggle.js import { getVisibleState, isFilterResult } from './indexTreeModel.js'; import { ICollapseStateChangeEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeEvent, ITreeFilter, ITreeModel, ITreeModelSpliceEvent, ITreeMouseEvent, ITreeNavigator, ITreeNode, ITreeRenderer, TreeDragOverBubble, TreeError, TreeFilterResult, TreeMouseEventTarget, TreeVisibility } from './tree.js'; import { Action } from '../../../common/actions.js'; -import { distinct, equals, range } from '../../../common/arrays.js'; +import { distinct, equals, insertInto, range } from '../../../common/arrays.js'; import { Delayer, disposableTimeout, timeout } from '../../../common/async.js'; import { Codicon } from '../../../common/codicons.js'; import { ThemeIcon } from '../../../common/themables.js'; @@ -3089,7 +3089,7 @@ export abstract class AbstractTree implements IDisposable state.expanded[getId(node.element)] = node.collapsed ? 0 : 1; } - queue.push(...node.children); + insertInto(queue, queue.length, node.children); } return state; From fc3cc5b1c6eb2f56a8ff194cef8844da393652cf Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Thu, 12 Dec 2024 19:02:45 +0800 Subject: [PATCH 140/479] Fix custom task shell doesn't work without manually passing in "run command" arg/flag (#181760) --- .../tasks/browser/terminalTaskSystem.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 045a9e63b44..45a7b5d022c 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1111,7 +1111,6 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { color: task.configurationProperties.icon?.color || undefined, waitOnExit }; - let shellSpecified: boolean = false; const shellOptions: IShellConfiguration | undefined = task.command.options && task.command.options.shell; if (shellOptions) { if (shellOptions.executable) { @@ -1120,12 +1119,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig.args = undefined; } shellLaunchConfig.executable = await this._resolveVariable(variableResolver, shellOptions.executable); - shellSpecified = true; } if (shellOptions.args) { shellLaunchConfig.args = await this._resolveVariables(variableResolver, shellOptions.args.slice()); } } + const shellArgsSpecified: boolean = shellLaunchConfig.args !== undefined; if (shellLaunchConfig.args === undefined) { shellLaunchConfig.args = []; } @@ -1138,29 +1137,29 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { windowsShellArgs = true; // If we don't have a cwd, then the terminal uses the home dir. const userHome = await this._pathService.userHome(); - if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { - return undefined; - } - if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { - if (!shellSpecified) { + if (basename === 'cmd.exe') { + if ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath))) { + return undefined; + } + if (!shellArgsSpecified) { + toAdd.push('/d', '/c'); + } + } else if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { + if (!shellArgsSpecified) { toAdd.push('-Command'); } } else if ((basename === 'bash.exe') || (basename === 'zsh.exe')) { windowsShellArgs = false; - if (!shellSpecified) { + if (!shellArgsSpecified) { toAdd.push('-c'); } } else if (basename === 'wsl.exe') { - if (!shellSpecified) { + if (!shellArgsSpecified) { toAdd.push('-e'); } - } else { - if (!shellSpecified) { - toAdd.push('/d', '/c'); - } } } else { - if (!shellSpecified) { + if (!shellArgsSpecified) { // Under Mac remove -l to not start it as a login shell. if (platform === Platform.Platform.Mac) { // Background on -l on osx https://github.com/microsoft/vscode/issues/107563 @@ -1267,11 +1266,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const combinedShellArgs: string[] = Objects.deepClone(configuredShellArgs); shellCommandArgs.forEach(element => { const shouldAddShellCommandArg = configuredShellArgs.every((arg, index) => { - if ((arg.toLowerCase() === element) && (configuredShellArgs.length > index + 1)) { + const isDuplicated = arg.toLowerCase() === element.toLowerCase(); + if (isDuplicated && (configuredShellArgs.length > index + 1)) { // We can still add the argument, but only if not all of the following arguments begin with "-". return !configuredShellArgs.slice(index + 1).every(testArg => testArg.startsWith('-')); } else { - return arg.toLowerCase() !== element; + return !isDuplicated; } }); if (shouldAddShellCommandArg) { From d44a823588d5cf49676184848bfc1ad1fb37b062 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 12 Dec 2024 12:24:25 +0100 Subject: [PATCH 141/479] hide tree when showing message, no specal zindex for message (#235929) fixes https://github.com/microsoft/vscode/issues/209757 --- src/vs/workbench/contrib/outline/browser/outlinePane.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.css b/src/vs/workbench/contrib/outline/browser/outlinePane.css index 7cdae3e8811..86e1465b0b2 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.css +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.css @@ -25,13 +25,13 @@ opacity: 0.5; position: absolute; pointer-events: none; - z-index: 1; } .monaco-workbench .outline-pane.message .outline-message { display: inherit; } -.monaco-workbench .outline-pane.message .outline-progress { +.monaco-workbench .outline-pane.message .outline-progress, +.monaco-workbench .outline-pane.message .outline-tree { display: none; } From 8d21c9df9dcf5f2b80680d2ef8c1925eaae91f30 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 12 Dec 2024 12:25:29 +0100 Subject: [PATCH 142/479] validate preview range using model (#235932) fixes https://github.com/microsoft/vscode/issues/205611 --- .../contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts index 70c093ee9d8..ea3d4987eeb 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts @@ -229,7 +229,7 @@ export class GotoDefinitionAtPositionEditorContribution implements IEditorContri if (numberOfLinesInRange >= GotoDefinitionAtPositionEditorContribution.MAX_SOURCE_PREVIEW_LINES) { rangeToUse = this.getPreviewRangeBasedOnIndentation(textEditorModel, startLineNumber); } - + rangeToUse = textEditorModel.validateRange(rangeToUse); const previewValue = this.stripIndentationFromPreviewRange(textEditorModel, startLineNumber, rangeToUse); return previewValue; } From 1d3cd67ef43a4aebd2330ebd57cfd9e8f6745fa0 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:47:13 +0100 Subject: [PATCH 143/479] Fix aria-activedescendant attribute in suggestion dropdown (#235933) fixes https://github.com/microsoft/monaco-editor/issues/3961 --- src/vs/editor/contrib/suggest/browser/suggestWidget.ts | 4 ++-- .../editor/contrib/suggest/browser/suggestWidgetRenderer.ts | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 048572143b1..e1bf843a994 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -32,7 +32,7 @@ import { CompletionModel } from './completionModel.js'; import { ResizableHTMLElement } from '../../../../base/browser/ui/resizable/resizable.js'; import { CompletionItem, Context as SuggestContext, suggestWidgetStatusbarMenu } from './suggest.js'; import { canExpandCompletionItem, SuggestDetailsOverlay, SuggestDetailsWidget } from './suggestWidgetDetails.js'; -import { getAriaId, ItemRenderer } from './suggestWidgetRenderer.js'; +import { ItemRenderer } from './suggestWidgetRenderer.js'; import { getListStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { status } from '../../../../base/browser/ui/aria/aria.js'; @@ -434,7 +434,7 @@ export class SuggestWidget implements IDisposable { this.element.domNode.classList.remove('docs-side'); } - this.editor.setAriaOptions({ activeDescendant: getAriaId(index) }); + this.editor.setAriaOptions({ activeDescendant: this._list.getElementID(index) }); }).catch(onUnexpectedError); } diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts b/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts index 24fb6d3106f..3d0850da13b 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts @@ -25,10 +25,6 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js import { CompletionItem } from './suggest.js'; import { canExpandCompletionItem } from './suggestWidgetDetails.js'; -export function getAriaId(index: number): string { - return `suggest-aria-id:${index}`; -} - const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight, nls.localize('suggestMoreInfoIcon', 'Icon for more information in the suggest widget.')); const _completionItemColor = new class ColorExtractor { @@ -167,7 +163,6 @@ export class ItemRenderer implements IListRenderer Date: Thu, 12 Dec 2024 12:51:05 +0100 Subject: [PATCH 144/479] autoDetectColorScheme enabled: fix flicker on window reload (#235935) --- .../nativeHostColorSchemeService.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts index efb620ad13f..b22de86f799 100644 --- a/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts +++ b/src/vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService.ts @@ -9,10 +9,14 @@ import { InstantiationType, registerSingleton } from '../../../../platform/insta import { Disposable } from '../../../../base/common/lifecycle.js'; import { IHostColorSchemeService } from '../common/hostColorSchemeService.js'; import { INativeWorkbenchEnvironmentService } from '../../environment/electron-sandbox/environmentService.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { isBoolean, isObject } from '../../../../base/common/types.js'; import { IColorScheme } from '../../../../platform/window/common/window.js'; +import { ILifecycleService, StartupKind } from '../../lifecycle/common/lifecycle.js'; export class NativeHostColorSchemeService extends Disposable implements IHostColorSchemeService { + // we remember the last color scheme value to restore for reloaded window static readonly STORAGE_KEY = 'HostColorSchemeData'; declare readonly _serviceBrand: undefined; @@ -26,13 +30,18 @@ export class NativeHostColorSchemeService extends Disposable implements IHostCol constructor( @INativeHostService private readonly nativeHostService: INativeHostService, @INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, + @IStorageService private storageService: IStorageService, + @ILifecycleService lifecycleService: ILifecycleService ) { super(); // register listener with the OS this._register(this.nativeHostService.onDidChangeColorScheme(scheme => this.update(scheme))); - const initial = environmentService.window.colorScheme; + let initial = environmentService.window.colorScheme; + if (lifecycleService.startupKind === StartupKind.ReloadedWindow) { + initial = this.getStoredValue(initial); + } this.dark = initial.dark; this.highContrast = initial.highContrast; @@ -40,12 +49,27 @@ export class NativeHostColorSchemeService extends Disposable implements IHostCol this.nativeHostService.getOSColorScheme().then(scheme => this.update(scheme)); } + private getStoredValue(dftl: IColorScheme): IColorScheme { + const stored = this.storageService.get(NativeHostColorSchemeService.STORAGE_KEY, StorageScope.APPLICATION); + if (stored) { + try { + const scheme = JSON.parse(stored); + if (isObject(scheme) && isBoolean(scheme.highContrast) && isBoolean(scheme.dark)) { + return scheme as IColorScheme; + } + } catch (e) { + // ignore + } + } + return dftl; + } private update({ highContrast, dark }: IColorScheme) { if (dark !== this.dark || highContrast !== this.highContrast) { this.dark = dark; this.highContrast = highContrast; + this.storageService.store(NativeHostColorSchemeService.STORAGE_KEY, JSON.stringify({ highContrast, dark }), StorageScope.APPLICATION, StorageTarget.MACHINE); this._onDidChangeColorScheme.fire(); } } From a63f68cebd2132f624ae9d02230b646b005635bb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 12 Dec 2024 13:04:08 +0100 Subject: [PATCH 145/479] fix https://github.com/microsoft/vscode/issues/212089 (#235936) --- src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts index b48cef6910c..15dc3bf04e6 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts @@ -130,7 +130,7 @@ export abstract class SymbolNavigationAction extends EditorAction2 { let altAction: SymbolNavigationAction | null | undefined; if (references.referenceAt(model.uri, position)) { const altActionId = this._getAlternativeCommand(editor); - if (!SymbolNavigationAction._activeAlternativeCommands.has(altActionId) && SymbolNavigationAction._allSymbolNavigationCommands.has(altActionId)) { + if (altActionId !== undefined && !SymbolNavigationAction._activeAlternativeCommands.has(altActionId) && SymbolNavigationAction._allSymbolNavigationCommands.has(altActionId)) { altAction = SymbolNavigationAction._allSymbolNavigationCommands.get(altActionId)!; } } @@ -170,7 +170,7 @@ export abstract class SymbolNavigationAction extends EditorAction2 { protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string; - protected abstract _getAlternativeCommand(editor: IActiveCodeEditor): string; + protected abstract _getAlternativeCommand(editor: IActiveCodeEditor): string | undefined; protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues; @@ -761,7 +761,9 @@ class GenericGoToLocationAction extends SymbolNavigationAction { return this._gotoMultipleBehaviour ?? editor.getOption(EditorOption.gotoLocation).multipleReferences; } - protected _getAlternativeCommand() { return ''; } + protected _getAlternativeCommand(): undefined { + return undefined; + } } CommandsRegistry.registerCommand({ From 822dba4bbc0f9cfb294c8aa0cba1d8eab7e83209 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 12 Dec 2024 13:12:57 +0100 Subject: [PATCH 146/479] Don't try instantiating an `InlineEditWithChanges` when there are no effective changes (#235937) --- .../view/inlineEdits/inlineEditsViewAndDiffProducer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewAndDiffProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewAndDiffProducer.ts index ea3c17e867b..a4e52c22af7 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewAndDiffProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewAndDiffProducer.ts @@ -70,6 +70,10 @@ export class InlineEditsViewAndDiffProducer extends Disposable { const rangeStartPos = edit.range.getStartPosition(); const innerChanges = result.changes.flatMap(c => c.innerChanges!); + if (innerChanges.length === 0) { + // there are no changes + return undefined; + } function addRangeToPos(pos: Position, range: Range): Range { const start = TextLength.fromPosition(range.getStartPosition()); From a05c4434d70872d660e97a5f542b9ac2a20a5003 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:17:16 +0100 Subject: [PATCH 147/479] Hover - fix input box hover (#235939) --- src/vs/base/browser/ui/inputbox/inputBox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 2f427923269..5ccf89beee4 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -237,7 +237,7 @@ export class InputBox extends Widget { this.tooltip = tooltip; if (!this.hover.value) { this.hover.value = this._register(getBaseLayerHoverDelegate().setupDelayedHoverAtMouse(this.input, () => ({ - content: tooltip, + content: this.tooltip, appearance: { compact: true, } From f6e97b7c977d5043aacec308fadd4847d062988d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 12 Dec 2024 14:47:59 +0100 Subject: [PATCH 148/479] debt - go over todos (#235924) * debt - go over todos * fix hygiene --- build/lib/inlineMeta.js | 2 +- build/lib/inlineMeta.ts | 2 +- .../auxiliaryWindow/electron-main/auxiliaryWindow.ts | 6 +++--- .../files/node/watcher/nodejs/nodejsWatcherLib.ts | 8 ++++---- src/vs/workbench/browser/layout.ts | 5 +---- .../workbench/browser/parts/titlebar/titlebarActions.ts | 2 +- .../accessibility/browser/accessibilityConfiguration.ts | 2 +- .../chat/electron-sandbox/actions/voiceChatActions.ts | 4 ++-- .../multiDiffEditor/browser/multiDiffEditorInput.ts | 6 +++--- src/vs/workbench/contrib/output/browser/outputView.ts | 2 +- 10 files changed, 18 insertions(+), 21 deletions(-) diff --git a/build/lib/inlineMeta.js b/build/lib/inlineMeta.js index f1dbfa83a7e..5ec7e9e9c07 100644 --- a/build/lib/inlineMeta.js +++ b/build/lib/inlineMeta.js @@ -8,7 +8,7 @@ exports.inlineMeta = inlineMeta; const es = require("event-stream"); const path_1 = require("path"); const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; -// TODO@bpasero in order to inline `product.json`, more work is +// TODO in order to inline `product.json`, more work is // needed to ensure that we cover all cases where modifications // are done to the product configuration during build. There are // at least 2 more changes that kick in very late: diff --git a/build/lib/inlineMeta.ts b/build/lib/inlineMeta.ts index ef3987fc32e..dc061aca8d1 100644 --- a/build/lib/inlineMeta.ts +++ b/build/lib/inlineMeta.ts @@ -15,7 +15,7 @@ export interface IInlineMetaContext { const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; -// TODO@bpasero in order to inline `product.json`, more work is +// TODO in order to inline `product.json`, more work is // needed to ensure that we cover all cases where modifications // are done to the product configuration during build. There are // at least 2 more changes that kick in very late: diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts index 698f5c817a6..971d5cd6b2e 100644 --- a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts @@ -62,9 +62,9 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow { y: options.y, width: options.width, height: options.height, - // TODO@bpasero We currently do not support restoring fullscreen state for - // auxiliary windows because we do not get hold of the original `features` - // string that contains that info in `window-fullscreen`. However, we can + // We currently do not support restoring fullscreen state for auxiliary + // windows because we do not get hold of the original `features` string + // that contains that info in `window-fullscreen`. However, we can // probe the `options.show` value for whether the window should be maximized // or not because we never show maximized windows initially to reduce flicker. mode: options.show === false ? WindowMode.Maximized : WindowMode.Normal diff --git a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts index 69792c99623..a7c89ceb00e 100644 --- a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts +++ b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts @@ -147,10 +147,10 @@ export class NodeJSFileWatcherLibrary extends Disposable { private doWatchWithExistingWatcher(realPath: string, isDirectory: boolean, disposables: DisposableStore): boolean { if (isDirectory) { - // TODO@bpasero recursive watcher re-use is currently not enabled - // for when folders are watched. this is because the dispatching - // in the recursive watcher for non-recurive requests is optimized - // for file changes where we really only match on the exact path + // Recursive watcher re-use is currently not enabled for when + // folders are watched. this is because the dispatching in the + // recursive watcher for non-recurive requests is optimized for + // file changes where we really only match on the exact path // and not child paths. return false; } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 782d0f48f38..8d731c24025 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -707,11 +707,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Auxiliary Panel to restore if (this.isVisible(Parts.AUXILIARYBAR_PART)) { - let viewContainerToRestore = this.storageService.get(AuxiliaryBarPart.activePanelSettingsKey, StorageScope.WORKSPACE, this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.AuxiliaryBar)?.id); + const viewContainerToRestore = this.storageService.get(AuxiliaryBarPart.activePanelSettingsKey, StorageScope.WORKSPACE, this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.AuxiliaryBar)?.id); if (viewContainerToRestore) { - if (viewContainerToRestore === 'workbench.panel.chatSidebar') { - viewContainerToRestore = 'workbench.panel.chat'; // TODO@bpasero remove me after some months - } this.state.initialization.views.containerToRestore.auxiliaryBar = viewContainerToRestore; } else { this.stateModel.setRuntimeValue(LayoutStateKeys.AUXILIARYBAR_HIDDEN, true); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index 20e940cd05f..f84bf3f06b7 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -259,7 +259,7 @@ registerAction2(class ToggleEditorActions extends Action2 { } }); -if (isLinux && isNative) { // TODO@bpasero remove me later +if (isLinux && isNative) { registerAction2(class ToggleCustomTitleBar extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 840442b7bac..32eff1e1594 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -813,7 +813,7 @@ export class DynamicSpeechAccessibilityConfiguration extends Disposable implemen localize('accessibility.voice.autoSynthesize.auto', "When a screen reader is detected, disable the feature. Otherwise, enable the feature.") ], 'markdownDescription': localize('autoSynthesize', "Whether a textual response should automatically be read out aloud when speech was used as input. For example in a chat session, a response is automatically synthesized when voice was used as chat request."), - 'default': this.productService.quality !== 'stable' ? 'auto' : 'off', // TODO@bpasero decide on a default + 'default': this.productService.quality !== 'stable' ? 'auto' : 'off', 'tags': ['accessibility'] } } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 568f6a693cc..57310027bc9 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -373,7 +373,7 @@ class VoiceChatSessions { if (autoSynthesize === 'on' || autoSynthesize === 'auto' && !this.accessibilityService.isScreenReaderOptimized()) { let context: IVoiceChatSessionController | 'focused'; if (controller.context === 'inline') { - // TODO@bpasero this is ugly, but the lightweight inline chat turns into + // This is ugly, but the lightweight inline chat turns into // a different widget as soon as a response comes in, so we fallback to // picking up from the focused chat widget context = 'focused'; @@ -695,7 +695,7 @@ class ChatSynthesizerSessionController { const contextKeyService = accessor.get(IContextKeyService); let chatWidget = chatWidgetService.getWidgetBySessionId(response.session.sessionId); if (chatWidget?.location === ChatAgentLocation.Editor) { - // TODO@bpasero workaround for https://github.com/microsoft/vscode/issues/212785 + // workaround for https://github.com/microsoft/vscode/issues/212785 chatWidget = chatWidgetService.lastFocusedWidget; } diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 0ab8f25019b..35847cd444a 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -280,9 +280,9 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor override readonly closeHandler: IEditorCloseHandler = { - // TODO@bpasero TODO@hediet this is a workaround for - // not having a better way to figure out if the - // editors this input wraps around are opened or not + // This is a workaround for not having a better way + // to figure out if the editors this input wraps + // around are opened or not async confirm() { return ConfirmResult.DONT_SAVE; diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 3876b25ea9e..4d280d00857 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -164,7 +164,7 @@ class OutputEditor extends AbstractTextResourceEditor { @IEditorService editorService: IEditorService, @IFileService fileService: IFileService ) { - super(OUTPUT_VIEW_ID, editorGroupService.activeGroup /* TODO@bpasero this is wrong */, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService, fileService); + super(OUTPUT_VIEW_ID, editorGroupService.activeGroup /* this is not correct but pragmatic */, telemetryService, instantiationService, storageService, textResourceConfigurationService, themeService, editorGroupService, editorService, fileService); this.resourceContext = this._register(instantiationService.createInstance(ResourceContextKey)); } From 3b30f94f4f129e67be9b6c677aac582923da1fab Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 12 Dec 2024 05:54:20 -0800 Subject: [PATCH 149/479] Only allow shellCommand for high confidence Fixes #235899 --- src/vs/platform/terminal/common/capabilities/capabilities.ts | 1 + .../common/capabilities/commandDetectionCapability.ts | 4 ++++ src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 4 +++- .../contrib/terminal/common/terminalConfiguration.ts | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index b6d91be1408..6de15be87fa 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -167,6 +167,7 @@ export interface ICommandDetectionCapability { /** The command currently being executed, otherwise undefined. */ readonly executingCommand: string | undefined; readonly executingCommandObject: ITerminalCommand | undefined; + readonly executingCommandConfidence: 'low' | 'medium' | 'high' | undefined; /** The current cwd at the cursor's position. */ readonly cwd: string | undefined; readonly currentCommand: ICurrentPartialCommand | undefined; diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 422607b4de6..8f9d90cf49b 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -49,6 +49,10 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe } return undefined; } + get executingCommandConfidence(): 'low' | 'medium' | 'high' | undefined { + const casted = this._currentCommand as PartialTerminalCommand | ITerminalCommand; + return 'commandLineConfidence' in casted ? casted.commandLineConfidence : undefined; + } get currentCommand(): ICurrentPartialCommand { return this._currentCommand; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 6c11d5d3ccd..0a1646792a0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -2515,9 +2515,11 @@ export class TerminalLabelComputer extends Disposable { : (instance.fixedRows ? `\u2195${instance.fixedRows}` : ''), separator: { label: this._terminalConfigurationService.config.tabs.separator }, shellType: instance.shellType, - shellCommand: commandDetection?.executingCommand && promptInputModel + // Shell command requires high confidence + shellCommand: commandDetection?.executingCommand && commandDetection.executingCommandConfidence === 'high' && promptInputModel ? promptInputModel.value + nonTaskSpinner : undefined, + // Shell prompt input does not require high confidence as it's largely for VS Code developers shellPromptInput: commandDetection?.executingCommand && promptInputModel ? promptInputModel.getCombinedString(true) + nonTaskSpinner : promptInputModel?.getCombinedString(true), diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 54c79186f02..beea86681fb 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -25,7 +25,7 @@ const terminalDescriptors = '\n- ' + [ '`\${sequence}`: ' + localize('sequence', "the name provided to the terminal by the process"), '`\${task}`: ' + localize('task', "indicates this terminal is associated with a task"), '`\${shellType}`: ' + localize('shellType', "the detected shell type"), - '`\${shellCommand}`: ' + localize('shellCommand', "the command being executed according to shell integration"), + '`\${shellCommand}`: ' + localize('shellCommand', "the command being executed according to shell integration. This also requires high confidence in the detected command line which may not work in some prompt frameworks."), '`\${shellPromptInput}`: ' + localize('shellPromptInput', "the shell's full prompt input according to shell integration"), ].join('\n- '); // intentionally concatenated to not produce a string that is too long for translations From a212efb7a6f3e6145c7e9a010c1fc2b13ec0099c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 12 Dec 2024 15:19:37 +0100 Subject: [PATCH 150/479] chat - trigger commands to refresh tokens as needed (#235952) --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 37544c7ec93..3af2ce5f98c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -266,7 +266,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const entitlement = await that.requests.forceResolveEntitlement(undefined); if (entitlement === ChatEntitlement.Pro) { - commandService.executeCommand('github.copilot.refreshToken'); // ugly, but we need to signal to the extension that entitlements changed + refreshTokens(commandService); } } } @@ -787,7 +787,7 @@ class ChatSetupController extends Disposable { installResult = isCancellationError(error) ? 'cancelled' : 'failedInstall'; } finally { if (wasInstalled && didSignUp) { - this.commandService.executeCommand('github.copilot.refreshToken'); // ugly, but we need to signal to the extension that sign-up happened + refreshTokens(this.commandService); } if (installResult === 'installed') { @@ -1088,3 +1088,9 @@ function showCopilotView(viewsService: IViewsService, layoutService: IWorkbenchL return showChatView(viewsService); } } + +function refreshTokens(commandService: ICommandService): void { + // ugly, but we need to signal to the extension that entitlements changed + commandService.executeCommand('github.copilot.signIn'); + commandService.executeCommand('github.copilot.refreshToken'); +} From 7c3b79da7f8ca28e61c57d17751f9494b5d6649c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 12 Dec 2024 16:01:23 +0100 Subject: [PATCH 151/479] inline edit view refactoring (#235836) * inline edit view refactoring * Allows NES to overflow. * Fixes file name casing * Adds missing file * fixes types * Fixes type error --- src/vs/base/browser/dom.ts | 9 +- .../browser/view/inlineCompletionsView.ts | 2 +- ...EditsIndicatorView.ts => indicatorView.ts} | 14 +- .../view/inlineEdits/inlineEditsView.ts | 557 ------------------ .../view/inlineEdits/sideBySideDiff.ts | 531 +++++++++++++++++ .../browser/view/inlineEdits/utils.ts | 270 ++++++++- .../{inlineEditsView.css => view.css} | 2 - .../browser/view/inlineEdits/view.ts | 143 +++++ ...DiffProducer.ts => viewAndDiffProducer.ts} | 2 +- 9 files changed, 955 insertions(+), 575 deletions(-) rename src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/{inlineEditsIndicatorView.ts => indicatorView.ts} (90%) delete mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts rename src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/{inlineEditsView.css => view.css} (98%) create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts rename src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/{inlineEditsViewAndDiffProducer.ts => viewAndDiffProducer.ts} (99%) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index ecf1d194cdc..65a672b0d8c 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -2189,22 +2189,23 @@ export function h(tag: string, ...args: [] | [attributes: { $: string } & Partia return result; } +/** @deprecated This is a duplication of the h function. Needs cleanup. */ export function svgElem (tag: TTag): TagToRecord extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; - +/** @deprecated This is a duplication of the h function. Needs cleanup. */ export function svgElem (tag: TTag, children: [...T]): (ArrayToObj & TagToRecord) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; - +/** @deprecated This is a duplication of the h function. Needs cleanup. */ export function svgElem (tag: TTag, attributes: Partial>>): TagToRecord extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; - +/** @deprecated This is a duplication of the h function. Needs cleanup. */ export function svgElem (tag: TTag, attributes: Partial>>, children: [...T]): (ArrayToObj & TagToRecord) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; - +/** @deprecated This is a duplication of the h function. Needs cleanup. */ export function svgElem(tag: string, ...args: [] | [attributes: { $: string } & Partial> | Record, children?: any[]] | [children: any[]]): Record { let attributes: { $?: string } & Partial>; let children: (Record | HTMLElement)[] | undefined; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts index 59773742bbb..baaf3c08205 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts @@ -15,7 +15,7 @@ import { InlineCompletionsHintsWidget } from '../hintsWidget/inlineCompletionsHi import { InlineCompletionsModel } from '../model/inlineCompletionsModel.js'; import { convertItemsToStableObservables } from '../utils.js'; import { GhostTextView } from './ghostText/ghostTextView.js'; -import { InlineEditsViewAndDiffProducer } from './inlineEdits/inlineEditsViewAndDiffProducer.js'; +import { InlineEditsViewAndDiffProducer } from './inlineEdits/viewAndDiffProducer.js'; export class InlineCompletionsView extends Disposable { private readonly _ghostTexts = derived(this, (reader) => { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts similarity index 90% rename from src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsIndicatorView.ts rename to src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts index d6aa3988cca..052451f326d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts @@ -13,10 +13,9 @@ import { registerColor } from '../../../../../../platform/theme/common/colorUtil import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { OffsetRange } from '../../../../../common/core/offsetRange.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; -import { Point } from './utils.js'; export interface IInlineEditsIndicatorState { - editTopLeft: Point; + editTop: number; showAlways: boolean; } @@ -73,15 +72,14 @@ export class InlineEditsIndicator extends Disposable { const range = new OffsetRange(0, i.height - 30); - const topEdit = state.editTopLeft; - this._indicator.root.classList.toggle('top', topEdit.y < range.start); - this._indicator.root.classList.toggle('bottom', topEdit.y > range.endExclusive); + const topEdit = state.editTop; + this._indicator.root.classList.toggle('top', topEdit < range.start); + this._indicator.root.classList.toggle('bottom', topEdit > range.endExclusive); const showAnyway = state.showAlways; this._indicator.root.classList.toggle('visible', showAnyway); - this._indicator.root.classList.toggle('contained', range.contains(topEdit.y)); + this._indicator.root.classList.toggle('contained', range.contains(topEdit)); - - this._indicator.root.style.top = `${range.clip(topEdit.y)}px`; + this._indicator.root.style.top = `${range.clip(topEdit)}px`; this._indicator.root.style.right = `${i.minimap.minimapWidth + i.verticalScrollbarWidth}px`; })); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts deleted file mode 100644 index 8b081fe0c11..00000000000 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ /dev/null @@ -1,557 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { h, svgElem } from '../../../../../../base/browser/dom.js'; -import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { autorun, constObservable, derived, derivedOpts, IObservable, observableFromEvent } from '../../../../../../base/common/observable.js'; -import { MenuId, MenuItemAction } from '../../../../../../platform/actions/common/actions.js'; -import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; -import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; -import { EmbeddedCodeEditorWidget } from '../../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; -import { appendRemoveOnDispose } from '../../../../../browser/widget/diffEditor/utils.js'; -import { EditorOption } from '../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; -import { Range } from '../../../../../common/core/range.js'; -import { StringText } from '../../../../../common/core/textEdit.js'; -import { lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; -import { TextModel } from '../../../../../common/model/textModel.js'; -import './inlineEditsView.css'; -import { IOriginalEditorInlineDiffViewState, OriginalEditorInlineDiffView } from './inlineDiffView.js'; -import { applyEditToModifiedRangeMappings, createReindentEdit, getOffsetForPos, maxContentWidthInRange, PathBuilder, Point, StatusBarViewItem } from './utils.js'; -import { IInlineEditsIndicatorState, InlineEditsIndicator } from './inlineEditsIndicatorView.js'; -import { darken, lighten, registerColor } from '../../../../../../platform/theme/common/colorUtils.js'; -import { diffInserted, diffRemoved } from '../../../../../../platform/theme/common/colorRegistry.js'; -import { CustomizedMenuWorkbenchToolBar } from '../../hintsWidget/inlineCompletionsHintsWidget.js'; -import { Command } from '../../../../../common/languages.js'; -import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; -import { structuralEquals } from '../../../../../../base/common/equals.js'; -import { IAction } from '../../../../../../base/common/actions.js'; -import { editorLineHighlightBorder } from '../../../../../common/core/editorColorRegistry.js'; -import { ActionViewItem } from '../../../../../../base/browser/ui/actionbar/actionViewItems.js'; -import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; -import { InlineEditWithChanges } from './inlineEditsViewAndDiffProducer.js'; -import { OffsetRange } from '../../../../../common/core/offsetRange.js'; -import { Color } from '../../../../../../base/common/color.js'; - -export const originalBackgroundColor = registerColor( - 'inlineEdit.originalBackground', - Color.transparent, - '', - true -); -export const modifiedBackgroundColor = registerColor( - 'inlineEdit.modifiedBackground', - Color.transparent, - '', - true -); - -export const originalChangedLineBackgroundColor = registerColor( - 'inlineEdit.originalChangedLineBackground', - Color.transparent, - '', - true -); - -export const originalChangedTextOverlayColor = registerColor( - 'inlineEdit.originalChangedTextBackground', - diffRemoved, - '', - true -); - -export const modifiedChangedLineBackgroundColor = registerColor( - 'inlineEdit.modifiedChangedLineBackground', - Color.transparent, - '', - true -); - -export const modifiedChangedTextOverlayColor = registerColor( - 'inlineEdit.modifiedChangedTextBackground', - diffInserted, - '', - true -); - -export const originalBorder = registerColor( - 'inlineEdit.originalBorder', - { - light: darken(editorLineHighlightBorder, 0.15), - dark: lighten(editorLineHighlightBorder, 0.50), - hcDark: editorLineHighlightBorder, - hcLight: editorLineHighlightBorder - }, - '' -); - -export const modifiedBorder = registerColor( - 'inlineEdit.modifiedBorder', - { - light: darken(editorLineHighlightBorder, 0.15), - dark: lighten(editorLineHighlightBorder, 0.50), - hcDark: editorLineHighlightBorder, - hcLight: editorLineHighlightBorder - }, - '' -); - -export class InlineEditsView extends Disposable { - private readonly _editorObs = observableCodeEditor(this._editor); - - private readonly _elements = h('div.inline-edits-view', { - style: { - position: 'absolute', - overflow: 'visible', - top: '0px', - left: '0px', - }, - }, [ - svgElem('svg@svg', { transform: 'translate(-0.5 -0.5)', style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, }, []), - h('div.editorContainer@editorContainer', { style: { position: 'absolute' } }, [ - h('div.preview@editor', { style: {} }), - h('div.toolbar@toolbar', { style: {} }), - ]), - svgElem('svg@svg2', { transform: 'translate(-0.5 -0.5)', style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, }, []), - ]); - - private readonly _useMixedLinesDiff = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useMixedLinesDiff); - private readonly _useInterleavedLinesDiff = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useInterleavedLinesDiff); - - constructor( - private readonly _editor: ICodeEditor, - private readonly _edit: IObservable, - private readonly _model: IObservable, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ICommandService private readonly _commandService: ICommandService, - ) { - super(); - - this._register(appendRemoveOnDispose(this._editor.getDomNode()!, this._elements.root)); - - this._register(this._editorObs.createOverlayWidget({ - domNode: this._elements.root, - position: constObservable(null), - allowEditorOverflow: false, - minContentWidthInPx: derived(reader => { - const x = this._previewEditorLayoutInfo.read(reader)?.maxContentWidth; - if (x === undefined) { return 0; } - return x; - }), - })); - - this._previewEditor.setModel(this._previewTextModel); - - - this._register(autorun(reader => { - const layoutInfo = this._previewEditorLayoutInfo.read(reader); - if (!layoutInfo) { - this._elements.svg.replaceChildren(); - return; - } - - const topEdit = layoutInfo.edit1; - const editHeight = layoutInfo.editHeight; - - const width = this._previewEditorWidth.read(reader); - - const pathBuilder1 = new PathBuilder(); - pathBuilder1.moveTo(layoutInfo.code2); - pathBuilder1.lineTo(layoutInfo.codeStart2); - pathBuilder1.lineTo(layoutInfo.codeStart1); - pathBuilder1.lineTo(layoutInfo.code1); - - - const pathBuilder2 = new PathBuilder(); - pathBuilder2.moveTo(layoutInfo.code1); - pathBuilder2.lineTo(layoutInfo.edit1); - pathBuilder2.lineTo(layoutInfo.edit1.deltaX(width)); - pathBuilder2.lineTo(layoutInfo.edit2.deltaX(width)); - pathBuilder2.lineTo(layoutInfo.edit2); - if (layoutInfo.edit2.y !== layoutInfo.code2.y) { - pathBuilder2.curveTo2(layoutInfo.edit2.deltaX(-20), layoutInfo.code2.deltaX(20), layoutInfo.code2.deltaX(0)); - } - pathBuilder2.lineTo(layoutInfo.code2); - - const pathBuilder4 = new PathBuilder(); - pathBuilder4.moveTo(layoutInfo.code1); - pathBuilder4.lineTo(layoutInfo.code1.deltaX(1000)); - pathBuilder4.lineTo(layoutInfo.code2.deltaX(1000)); - pathBuilder4.lineTo(layoutInfo.code2); - - const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path1.setAttribute('d', pathBuilder1.build()); - path1.style.fill = 'var(--vscode-inlineEdit-originalBackground, transparent)'; - path1.style.stroke = 'var(--vscode-inlineEdit-originalBorder)'; - path1.style.strokeWidth = '1px'; - - - const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path2.setAttribute('d', pathBuilder2.build()); - path2.style.fill = 'var(--vscode-inlineEdit-modifiedBackground, transparent)'; - path2.style.stroke = 'var(--vscode-inlineEdit-modifiedBorder)'; - path2.style.strokeWidth = '1px'; - - const pathModifiedBackground = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - pathModifiedBackground.setAttribute('d', pathBuilder2.build()); - pathModifiedBackground.style.fill = 'var(--vscode-editor-background, transparent)'; - pathModifiedBackground.style.strokeWidth = '1px'; - - - - const elements: SVGElement[] = []; - if (layoutInfo.shouldShowShadow) { - const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); - const linearGradient = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient'); - linearGradient.setAttribute('id', 'gradient'); - linearGradient.setAttribute('x1', '0%'); - linearGradient.setAttribute('x2', '100%'); - - const stop1 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); - stop1.setAttribute('offset', '0%'); - stop1.setAttribute('style', 'stop-color:var(--vscode-inlineEdit-modifiedBorder);stop-opacity:0'); - - const stop2 = document.createElementNS('http://www.w3.org/2000/svg', 'stop'); - stop2.setAttribute('offset', '100%'); - stop2.setAttribute('style', 'stop-color:var(--vscode-inlineEdit-modifiedBorder);stop-opacity:1'); - - linearGradient.appendChild(stop1); - linearGradient.appendChild(stop2); - defs.appendChild(linearGradient); - - const width = 6; - const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); - rect.setAttribute('x', `${layoutInfo.code1.x - width}`); - rect.setAttribute('y', `${layoutInfo.code1.y}`); - rect.setAttribute('width', `${width}`); - rect.setAttribute('height', `${layoutInfo.code2.y - layoutInfo.code1.y}`); - rect.setAttribute('fill', 'url(#gradient)'); - rect.style.strokeWidth = '0'; - rect.style.stroke = 'transparent'; - - elements.push(defs); - elements.push(rect); - } else { - const pathBuilder3 = new PathBuilder(); - pathBuilder3.moveTo(layoutInfo.code1); - pathBuilder3.lineTo(layoutInfo.code2); - - const path3 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path3.setAttribute('d', pathBuilder3.build()); - path3.style.stroke = 'var(--vscode-inlineEdit-modifiedBorder)'; - path3.style.strokeWidth = '1px'; - elements.push(path3); - } - - const path4 = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - path4.setAttribute('d', pathBuilder4.build()); - path4.style.fill = 'var(--vscode-editor-background, transparent)'; - - this._elements.svg.replaceChildren(path4, pathModifiedBackground); - this._elements.svg2.replaceChildren(path1, path2, ...elements); - - this._elements.editorContainer.style.top = `${topEdit.y}px`; - this._elements.editorContainer.style.left = `${topEdit.x}px`; - - this._previewEditor.layout({ height: editHeight, width }); - })); - - const toolbarDropdownVisible = observableFromEvent(this, this._toolbar.onDidChangeDropdownVisibility, (e) => e ?? false); - - this._register(autorun(reader => { - this._elements.root.classList.toggle('toolbarDropdownVisible', toolbarDropdownVisible.read(reader)); - })); - } - - private readonly _uiState = derived(this, reader => { - const edit = this._edit.read(reader); - if (!edit) { return undefined; } - - this._model.get()?.handleInlineCompletionShown(edit.inlineCompletion); - - let mappings = RangeMapping.fromEdit(edit.edit); - let newText = edit.edit.apply(edit.originalText); - let diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); - - let state: 'collapsed' | 'mixedLines' | 'interleavedLines' | 'sideBySide'; - if (edit.isCollapsed) { - state = 'collapsed'; - } else if (diff.every(m => OriginalEditorInlineDiffView.supportsInlineDiffRendering(m)) && - (this._useMixedLinesDiff.read(reader) === 'whenPossible' || (edit.userJumpedToIt && this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible'))) { - state = 'mixedLines'; - } else if ((this._useInterleavedLinesDiff.read(reader) === 'always' || (edit.userJumpedToIt && this._useInterleavedLinesDiff.read(reader) === 'afterJump'))) { - state = 'interleavedLines'; - } else { - state = 'sideBySide'; - } - - if (state === 'sideBySide') { - const indentationAdjustmentEdit = createReindentEdit(newText, edit.modifiedLineRange); - newText = indentationAdjustmentEdit.applyToString(newText); - - mappings = applyEditToModifiedRangeMappings(mappings, indentationAdjustmentEdit); - diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); - } - - const originalDisplayRange = edit.originalText.lineRange.intersect( - edit.originalLineRange.join( - LineRange.ofLength(edit.originalLineRange.startLineNumber, edit.lineEdit.newLines.length) - ) - )!; - - return { - state, - diff, - edit, - newText, - newTextLineCount: edit.modifiedLineRange.length, - originalDisplayRange: originalDisplayRange, - }; - }); - - protected readonly _toolbar = this._register(this._instantiationService.createInstance(CustomizedMenuWorkbenchToolBar, this._elements.toolbar, MenuId.InlineEditsActions, { - menuOptions: { renderShortTitle: true }, - toolbarOptions: { - primaryGroup: g => g.startsWith('primary'), - }, - actionViewItemProvider: (action, options) => { - if (action instanceof MenuItemAction) { - return this._instantiationService.createInstance(StatusBarViewItem, action, undefined); - } - if (action.class === undefined) { - return this._instantiationService.createInstance(ActionViewItem, {}, action, { icon: false }); - } - return undefined; - }, - telemetrySource: 'inlineEditsToolbar' - })); - - private readonly _extraCommands = derivedOpts({ owner: this, equalsFn: structuralEquals }, reader => { - return this._uiState.read(reader)?.edit.commands ?? []; - }); - - protected readonly _updateToolbarAutorun = this._register(autorun(reader => { - /** @description extra commands */ - const extraCommands = this._extraCommands.read(reader); - const primaryExtraActions: IAction[] = []; - const secondaryExtraActions: IAction[] = []; - for (const c of extraCommands) { - const action: IAction = { - class: undefined, - id: c.id, - enabled: true, - tooltip: c.tooltip || '', - label: c.title, - run: (event) => { - return this._commandService.executeCommand(c.id, ...(c.arguments ?? [])); - }, - }; - // TODO this is a hack just to make the feedback action more visible. - if (c.title.toLowerCase().indexOf('feedback') !== -1) { - primaryExtraActions.push(action); - } else { - secondaryExtraActions.push(action); - } - } - - this._toolbar.setAdditionalPrimaryActions(primaryExtraActions); - this._toolbar.setAdditionalSecondaryActions(secondaryExtraActions); - })); - - // #region preview editor - - private readonly _previewTextModel = this._register(this._instantiationService.createInstance( - TextModel, - '', - this._editor.getModel()!.getLanguageId(), - { ...TextModel.DEFAULT_CREATION_OPTIONS, bracketPairColorizationOptions: { enabled: true, independentColorPoolPerBracketType: false } }, - null - )); - - private readonly _previewEditor = this._register(this._instantiationService.createInstance( - EmbeddedCodeEditorWidget, - this._elements.editor, - { - glyphMargin: false, - lineNumbers: 'off', - minimap: { enabled: false }, - guides: { - indentation: false, - bracketPairs: false, - bracketPairsHorizontal: false, - highlightActiveIndentation: false, - }, - folding: false, - selectOnLineNumbers: false, - selectionHighlight: false, - columnSelection: false, - overviewRulerBorder: false, - overviewRulerLanes: 0, - lineDecorationsWidth: 0, - lineNumbersMinChars: 0, - bracketPairColorization: { enabled: true, independentColorPoolPerBracketType: false }, - scrollBeyondLastLine: false, - scrollbar: { - vertical: 'hidden', - horizontal: 'hidden', - handleMouseWheel: false, - }, - readOnly: true, - wordWrap: 'off', - }, - { contributions: [], }, - this._editor - )); - - private readonly _previewEditorObs = observableCodeEditor(this._previewEditor); - - private readonly _previewEditorRootVisibility = derived(this, reader => this._uiState.read(reader)?.state === 'sideBySide' ? 'block' : 'none'); - private readonly _updatePreviewEditorRootVisibility = derived(reader => { - this._elements.root.style.display = this._previewEditorRootVisibility.read(reader); - }); - - private readonly _updatePreviewEditor = derived(reader => { - this._updatePreviewEditorRootVisibility.read(reader); - - const uiState = this._uiState.read(reader); - if (!uiState) { return; } - - - this._previewTextModel.setLanguage(this._editor.getModel()!.getLanguageId()); - this._previewTextModel.setValue(uiState.newText); - const range = uiState.edit.originalLineRange; - - const hiddenAreas: Range[] = []; - if (range.startLineNumber > 1) { - hiddenAreas.push(new Range(1, 1, range.startLineNumber - 1, 1)); - } - if (range.startLineNumber + uiState.newTextLineCount < this._previewTextModel.getLineCount() + 1) { - hiddenAreas.push(new Range(range.startLineNumber + uiState.newTextLineCount, 1, this._previewTextModel.getLineCount() + 1, 1)); - } - - this._previewEditor.setHiddenAreas(hiddenAreas, undefined, true); - - }).recomputeInitiallyAndOnChange(this._store); - - private readonly _previewEditorWidth = derived(this, reader => { - const edit = this._edit.read(reader); - if (!edit) { return 0; } - this._updatePreviewEditor.read(reader); - - return maxContentWidthInRange(this._previewEditorObs, edit.modifiedLineRange, reader) + 10; - }); - - private readonly _cursorPosIfTouchesEdit = derived(this, reader => { - const cursorPos = this._editorObs.cursorPosition.read(reader); - const edit = this._edit.read(reader); - if (!edit || !cursorPos) { return undefined; } - return edit.modifiedLineRange.contains(cursorPos.lineNumber) ? cursorPos : undefined; - }); - - /** - * ![test](./layout.dio.svg) - */ - private readonly _previewEditorLayoutInfo = derived(this, (reader) => { - const inlineEdit = this._edit.read(reader); - if (!inlineEdit) { - return null; - } - const state = this._uiState.read(reader); - if (!state) { - return null; - } - - const range = inlineEdit.originalLineRange; - - const horizontalScrollOffset = this._editorObs.scrollLeft.read(reader); - - const editorContentMaxWidthInRange = maxContentWidthInRange(this._editorObs, state.originalDisplayRange, reader); - const editorLayout = this._editorObs.layoutInfo.read(reader); - const previewWidth = this._previewEditorWidth.read(reader); - const editorContentAreaWidth = editorLayout.width - editorLayout.contentLeft - editorLayout.minimap.minimapWidth - editorLayout.verticalScrollbarWidth; - - const cursorPos = this._cursorPosIfTouchesEdit.read(reader); - - const maxPreviewEditorLeft = Math.max( - editorContentAreaWidth * 0.65 + horizontalScrollOffset - 10, - editorContentAreaWidth - previewWidth - 70 + horizontalScrollOffset - 10, - cursorPos ? getOffsetForPos(this._editorObs, cursorPos, reader) + 50 : 0, - ); - const previewEditorLeftInTextArea = Math.min(editorContentMaxWidthInRange + 20, maxPreviewEditorLeft); - - const previewEditorLeft = editorLayout.contentLeft + previewEditorLeftInTextArea; - const maxContentWidth = editorContentMaxWidthInRange + 20 + previewWidth + 70; - - const dist = maxPreviewEditorLeft - previewEditorLeftInTextArea; - - const left = Math.max(editorLayout.contentLeft, previewEditorLeft - horizontalScrollOffset); - - const selectionTop = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); - const selectionBottom = this._editor.getTopForLineNumber(range.endLineNumberExclusive) - this._editorObs.scrollTop.read(reader); - - const codeLeft = editorLayout.contentLeft; - - const code1 = new Point(left, selectionTop); - const codeStart1 = new Point(codeLeft, selectionTop); - const code2 = new Point(left, selectionBottom); - const codeStart2 = new Point(codeLeft, selectionBottom); - const codeHeight = selectionBottom - selectionTop; - - const codeEditDistRange = - inlineEdit.modifiedLineRange.length === inlineEdit.originalLineRange.length - ? new OffsetRange(4, 61) - : new OffsetRange(60, 61); - - const clipped = dist === 0; - - const codeEditDist = codeEditDistRange.clip(dist); - const editHeight = this._editor.getOption(EditorOption.lineHeight) * inlineEdit.modifiedLineRange.length; - - const edit1 = new Point(left + codeEditDist, selectionTop); - const edit2 = new Point(left + codeEditDist, selectionTop + editHeight); - - return { - code1, - codeStart1, - code2, - codeStart2, - codeHeight, - - edit1, - edit2, - editHeight, - previewEditorLeft, - maxContentWidth, - shouldShowShadow: clipped, - }; - }); - - // #endregion - - private readonly _inlineDiffViewState = derived(this, reader => { - const e = this._uiState.read(reader); - if (!e) { return undefined; } - - return { - modifiedText: new StringText(e.newText), - diff: e.diff, - mode: e.state === 'collapsed' ? 'sideBySide' : e.state, - modifiedCodeEditor: this._previewEditor, - }; - }); - protected readonly _inlineDiffView = this._register(new OriginalEditorInlineDiffView(this._editor, this._inlineDiffViewState, this._previewTextModel)); - - protected readonly _indicator = this._register(new InlineEditsIndicator( - this._editorObs, - derived(reader => { - const state = this._uiState.read(reader); - const edit1 = this._previewEditorLayoutInfo.read(reader)?.edit1; - if (!edit1 || !state) { return undefined; } - return { editTopLeft: edit1, showAlways: state.state !== 'sideBySide' }; - }), - this._model, - )); -} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts new file mode 100644 index 00000000000..90f026719a3 --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -0,0 +1,531 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { ActionViewItem } from '../../../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { IAction } from '../../../../../../base/common/actions.js'; +import { Color } from '../../../../../../base/common/color.js'; +import { structuralEquals } from '../../../../../../base/common/equals.js'; +import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { IObservable, constObservable, derived, autorun, derivedOpts, observableValue } from '../../../../../../base/common/observable.js'; +import { MenuId, MenuItemAction } from '../../../../../../platform/actions/common/actions.js'; +import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { diffRemoved, diffInserted } from '../../../../../../platform/theme/common/colorRegistry.js'; +import { registerColor, darken, lighten } from '../../../../../../platform/theme/common/colorUtils.js'; +import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; +import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { EmbeddedCodeEditorWidget } from '../../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; +import { EditorOption } from '../../../../../common/config/editorOptions.js'; +import { editorLineHighlightBorder } from '../../../../../common/core/editorColorRegistry.js'; +import { LineRange } from '../../../../../common/core/lineRange.js'; +import { OffsetRange } from '../../../../../common/core/offsetRange.js'; +import { Range } from '../../../../../common/core/range.js'; +import { Command } from '../../../../../common/languages.js'; +import { ITextModel } from '../../../../../common/model.js'; +import { CustomizedMenuWorkbenchToolBar } from '../../hintsWidget/inlineCompletionsHintsWidget.js'; +import { InlineEditWithChanges } from './viewAndDiffProducer.js'; +import { StatusBarViewItem, maxContentWidthInRange, getOffsetForPos, Point, n, PathBuilder, mapOutFalsy } from './utils.js'; + + +export const originalBackgroundColor = registerColor( + 'inlineEdit.originalBackground', + Color.transparent, + '', + true +); +export const modifiedBackgroundColor = registerColor( + 'inlineEdit.modifiedBackground', + Color.transparent, + '', + true +); + +export const originalChangedLineBackgroundColor = registerColor( + 'inlineEdit.originalChangedLineBackground', + Color.transparent, + '', + true +); + +export const originalChangedTextOverlayColor = registerColor( + 'inlineEdit.originalChangedTextBackground', + diffRemoved, + '', + true +); + +export const modifiedChangedLineBackgroundColor = registerColor( + 'inlineEdit.modifiedChangedLineBackground', + Color.transparent, + '', + true +); + +export const modifiedChangedTextOverlayColor = registerColor( + 'inlineEdit.modifiedChangedTextBackground', + diffInserted, + '', + true +); + +export const originalBorder = registerColor( + 'inlineEdit.originalBorder', + { + light: darken(editorLineHighlightBorder, 0.15), + dark: lighten(editorLineHighlightBorder, 0.50), + hcDark: editorLineHighlightBorder, + hcLight: editorLineHighlightBorder + }, + '' +); + +export const modifiedBorder = registerColor( + 'inlineEdit.modifiedBorder', + { + light: darken(editorLineHighlightBorder, 0.15), + dark: lighten(editorLineHighlightBorder, 0.50), + hcDark: editorLineHighlightBorder, + hcLight: editorLineHighlightBorder + }, + '' +); + +export class InlineEditsSideBySideDiff extends Disposable { + private readonly _editorObs = observableCodeEditor(this._editor); + + constructor( + private readonly _editor: ICodeEditor, + private readonly _edit: IObservable, + private readonly _previewTextModel: ITextModel, + private readonly _uiState: IObservable<{ + edit: InlineEditWithChanges; + newTextLineCount: number; + originalDisplayRange: LineRange; + } | undefined>, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ICommandService private readonly _commandService: ICommandService + ) { + super(); + + this._register(this._editorObs.createOverlayWidget({ + domNode: this._overflowView.element, + position: constObservable(null), + allowEditorOverflow: true, + minContentWidthInPx: constObservable(0), + })); + + this._register(this._editorObs.createOverlayWidget({ + domNode: this._nonOverflowView.element, + position: constObservable(null), + allowEditorOverflow: false, + minContentWidthInPx: derived(reader => { + const x = this._previewEditorLayoutInfo.read(reader)?.maxContentWidth; + if (x === undefined) { return 0; } + return x; + }), + })); + + this.previewEditor.setModel(this._previewTextModel); + + this._register(autorun(reader => { + + const layoutInfo = this._previewEditorLayoutInfo.read(reader); + if (!layoutInfo) { + //this._elements.svg.replaceChildren(); + return; + } + + const editHeight = layoutInfo.editHeight; + const width = this._previewEditorWidth.read(reader); + this.previewEditor.layout({ height: editHeight, width }); + + const topEdit = layoutInfo.edit1; + this._editorContainer.element.style.top = `${topEdit.y}px`; + this._editorContainer.element.style.left = `${topEdit.x}px`; + })); + + /*const toolbarDropdownVisible = observableFromEvent(this, this._toolbar.onDidChangeDropdownVisibility, (e) => e ?? false); + + this._register(autorun(reader => { + this._elements.root.classList.toggle('toolbarDropdownVisible', toolbarDropdownVisible.read(reader)); + }));*/ + + this._editorContainerTopLeft.set(this._previewEditorLayoutInfo.map(i => i?.edit1), undefined); + } + + private readonly previewRef = n.ref(); + private readonly toolbarRef = n.ref(); + + private readonly _editorContainerTopLeft = observableValue | undefined>(this, undefined); + + private readonly _editorContainer = n.div({ + class: 'editorContainer', + style: { position: 'absolute' }, + }, [ + n.div({ class: 'preview', style: {}, ref: this.previewRef }), + n.div({ class: 'toolbar', style: {}, ref: this.toolbarRef }), + ]).keepUpdated(this._store); + + protected readonly _toolbar = this._register(this._instantiationService.createInstance(CustomizedMenuWorkbenchToolBar, this.toolbarRef.element, MenuId.InlineEditsActions, { + menuOptions: { renderShortTitle: true }, + toolbarOptions: { + primaryGroup: g => g.startsWith('primary'), + }, + actionViewItemProvider: (action, options) => { + if (action instanceof MenuItemAction) { + return this._instantiationService.createInstance(StatusBarViewItem, action, undefined); + } + if (action.class === undefined) { + return this._instantiationService.createInstance(ActionViewItem, {}, action, { icon: false }); + } + return undefined; + }, + telemetrySource: 'inlineEditsToolbar' + })); + + private readonly _extraCommands = derivedOpts({ owner: this, equalsFn: structuralEquals }, reader => { + return this._uiState.read(reader)?.edit.commands ?? []; + }); + + protected readonly _updateToolbarAutorun = this._register(autorun(reader => { + /** @description extra commands */ + const extraCommands = this._extraCommands.read(reader); + const primaryExtraActions: IAction[] = []; + const secondaryExtraActions: IAction[] = []; + for (const c of extraCommands) { + const action: IAction = { + class: undefined, + id: c.id, + enabled: true, + tooltip: c.tooltip || '', + label: c.title, + run: (event) => { + return this._commandService.executeCommand(c.id, ...(c.arguments ?? [])); + }, + }; + // TODO this is a hack just to make the feedback action more visible. + if (c.title.toLowerCase().indexOf('feedback') !== -1) { + primaryExtraActions.push(action); + } else { + secondaryExtraActions.push(action); + } + } + + this._toolbar.setAdditionalPrimaryActions(primaryExtraActions); + this._toolbar.setAdditionalSecondaryActions(secondaryExtraActions); + })); + + public readonly previewEditor = this._register(this._instantiationService.createInstance( + EmbeddedCodeEditorWidget, + this.previewRef.element, + { + glyphMargin: false, + lineNumbers: 'off', + minimap: { enabled: false }, + guides: { + indentation: false, + bracketPairs: false, + bracketPairsHorizontal: false, + highlightActiveIndentation: false, + }, + folding: false, + selectOnLineNumbers: false, + selectionHighlight: false, + columnSelection: false, + overviewRulerBorder: false, + overviewRulerLanes: 0, + lineDecorationsWidth: 0, + lineNumbersMinChars: 0, + bracketPairColorization: { enabled: true, independentColorPoolPerBracketType: false }, + scrollBeyondLastLine: false, + scrollbar: { + vertical: 'hidden', + horizontal: 'hidden', + handleMouseWheel: false, + }, + readOnly: true, + wordWrap: 'off', + }, + { contributions: [], }, + this._editor + )); + + private readonly _previewEditorObs = observableCodeEditor(this.previewEditor); + + private readonly _updatePreviewEditor = derived(reader => { + this._editorContainer.readEffect(reader); + + const uiState = this._uiState.read(reader); + if (!uiState) { + return; + } + + const range = uiState.edit.originalLineRange; + + const hiddenAreas: Range[] = []; + if (range.startLineNumber > 1) { + hiddenAreas.push(new Range(1, 1, range.startLineNumber - 1, 1)); + } + if (range.startLineNumber + uiState.newTextLineCount < this._previewTextModel.getLineCount() + 1) { + hiddenAreas.push(new Range(range.startLineNumber + uiState.newTextLineCount, 1, this._previewTextModel.getLineCount() + 1, 1)); + } + + this.previewEditor.setHiddenAreas(hiddenAreas, undefined, true); + + }).recomputeInitiallyAndOnChange(this._store); + + private readonly _previewEditorWidth = derived(this, reader => { + const edit = this._edit.read(reader); + if (!edit) { return 0; } + this._updatePreviewEditor.read(reader); + + return maxContentWidthInRange(this._previewEditorObs, edit.modifiedLineRange, reader) + 10; + }); + + private readonly _cursorPosIfTouchesEdit = derived(this, reader => { + const cursorPos = this._editorObs.cursorPosition.read(reader); + const edit = this._edit.read(reader); + if (!edit || !cursorPos) { return undefined; } + return edit.modifiedLineRange.contains(cursorPos.lineNumber) ? cursorPos : undefined; + }); + + /** + * ![test](./layout.dio.svg) + */ + private readonly _previewEditorLayoutInfo = derived(this, (reader) => { + const inlineEdit = this._edit.read(reader); + if (!inlineEdit) { + return null; + } + const state = this._uiState.read(reader); + if (!state) { + return null; + } + + const range = inlineEdit.originalLineRange; + + const horizontalScrollOffset = this._editorObs.scrollLeft.read(reader); + + const editorContentMaxWidthInRange = maxContentWidthInRange(this._editorObs, state.originalDisplayRange, reader); + const editorLayout = this._editorObs.layoutInfo.read(reader); + const previewWidth = this._previewEditorWidth.read(reader); + const editorContentAreaWidth = editorLayout.width - editorLayout.contentLeft - editorLayout.minimap.minimapWidth - editorLayout.verticalScrollbarWidth; + + const cursorPos = this._cursorPosIfTouchesEdit.read(reader); + + const maxPreviewEditorLeft = Math.max( + editorContentAreaWidth * 0.65 + horizontalScrollOffset - 10, + editorContentAreaWidth - previewWidth - 70 + horizontalScrollOffset - 10, + cursorPos ? getOffsetForPos(this._editorObs, cursorPos, reader) + 50 : 0 + ); + const previewEditorLeftInTextArea = Math.min(editorContentMaxWidthInRange + 20, maxPreviewEditorLeft); + + const previewEditorLeft = editorLayout.contentLeft + previewEditorLeftInTextArea; + const maxContentWidth = editorContentMaxWidthInRange + 20 + previewWidth + 70; + + const dist = maxPreviewEditorLeft - previewEditorLeftInTextArea; + + const left = Math.max(editorLayout.contentLeft, previewEditorLeft - horizontalScrollOffset); + + const selectionTop = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); + const selectionBottom = this._editor.getTopForLineNumber(range.endLineNumberExclusive) - this._editorObs.scrollTop.read(reader); + + const codeLeft = editorLayout.contentLeft; + + const code1 = new Point(left, selectionTop); + const codeStart1 = new Point(codeLeft, selectionTop); + const code2 = new Point(left, selectionBottom); + const codeStart2 = new Point(codeLeft, selectionBottom); + const codeHeight = selectionBottom - selectionTop; + + const codeEditDistRange = inlineEdit.modifiedLineRange.length === inlineEdit.originalLineRange.length + ? new OffsetRange(4, 61) + : new OffsetRange(60, 61); + + const clipped = dist === 0; + + const codeEditDist = codeEditDistRange.clip(dist); + const editHeight = this._editor.getOption(EditorOption.lineHeight) * inlineEdit.modifiedLineRange.length; + + const edit1 = new Point(left + codeEditDist, selectionTop); + const edit2 = new Point(left + codeEditDist, selectionTop + editHeight); + + return { + code1, + codeStart1, + code2, + codeStart2, + codeHeight, + + edit1, + edit2, + editHeight, + previewEditorLeft, + maxContentWidth, + shouldShowShadow: clipped, + }; + }); + + private readonly _shouldOverflow = derived(reader => { + const range = this._edit.read(reader)?.originalLineRange; + if (!range) { + return false; + } + const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); + return top > 0; + }); + + + private readonly _extendedModifiedPath = derived(reader => { + const layoutInfo = this._previewEditorLayoutInfo.read(reader); + if (!layoutInfo) { return undefined; } + const width = this._previewEditorWidth.read(reader); + const extendedModifiedPathBuilder = new PathBuilder() + .moveTo(layoutInfo.code1) + .lineTo(layoutInfo.edit1) + .lineTo(layoutInfo.edit1.deltaX(width)) + .lineTo(layoutInfo.edit2.deltaX(width)) + .lineTo(layoutInfo.edit2); + if (layoutInfo.edit2.y !== layoutInfo.code2.y) { + extendedModifiedPathBuilder.curveTo2(layoutInfo.edit2.deltaX(-20), layoutInfo.code2.deltaX(20), layoutInfo.code2.deltaX(0)); + } + extendedModifiedPathBuilder.lineTo(layoutInfo.code2); + return extendedModifiedPathBuilder.build(); + }); + + private readonly _backgroundSvg = n.svg({ + transform: 'translate(-0.5 -0.5)', + style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, + }, [ + n.svgElem('path', { + class: 'extendedModifiedBackgroundCoverUp', + d: this._extendedModifiedPath, + style: { + fill: 'var(--vscode-editor-background, transparent)', + strokeWidth: '1px', + } + }), + n.svgElem('path', { + class: 'rightOfModifiedBackgroundCoverUp', + d: this._previewEditorLayoutInfo.map(layoutInfo => layoutInfo && new PathBuilder() + .moveTo(layoutInfo.code1) + .lineTo(layoutInfo.code1.deltaX(1000)) + .lineTo(layoutInfo.code2.deltaX(1000)) + .lineTo(layoutInfo.code2) + .build() + ), + style: { + fill: 'var(--vscode-editor-background, transparent)', + } + }), + ]).keepUpdated(this._store); + + private readonly _foregroundSvg = n.svg({ + transform: 'translate(-0.5 -0.5)', + style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, + }, derived(reader => { + const layoutInfoObs = mapOutFalsy(this._previewEditorLayoutInfo).read(reader); + if (!layoutInfoObs) { return undefined; } + + const shadowWidth = 6; + return [ + n.svgElem('path', { + class: 'originalOverlay', + d: layoutInfoObs.map(layoutInfo => new PathBuilder() + .moveTo(layoutInfo.code2) + .lineTo(layoutInfo.codeStart2) + .lineTo(layoutInfo.codeStart1) + .lineTo(layoutInfo.code1) + .build() + ), + style: { + fill: 'var(--vscode-inlineEdit-originalBackground, transparent)', + stroke: 'var(--vscode-inlineEdit-originalBorder)', + strokeWidth: '1px', + } + }), + + n.svgElem('path', { + class: 'extendedModifiedOverlay', + d: this._extendedModifiedPath, + style: { + fill: 'var(--vscode-inlineEdit-modifiedBackground, transparent)', + stroke: 'var(--vscode-inlineEdit-modifiedBorder)', + strokeWidth: '1px', + } + }), + + ...(!layoutInfoObs.map(i => i.shouldShowShadow).read(reader) + ? [ + n.svgElem('path', { + class: 'middleBorder', + d: layoutInfoObs.map(layoutInfo => new PathBuilder() + .moveTo(layoutInfo.code1) + .lineTo(layoutInfo.code2) + .build() + ), + style: { + stroke: 'var(--vscode-inlineEdit-modifiedBorder)', + strokeWidth: '1px' + } + }) + ] + : [ + n.svgElem('defs', {}, [ + n.svgElem('linearGradient', { id: 'gradient', x1: '0%', x2: '100%', }, [ + n.svgElem('stop', { + offset: '0%', + style: { stopColor: 'var(--vscode-inlineEdit-modifiedBorder)', stopOpacity: '0', } + }), + n.svgElem('stop', { + offset: '100%', + style: { stopColor: 'var(--vscode-inlineEdit-modifiedBorder)', stopOpacity: '1', } + }) + ]) + ]), + n.svgElem('rect', { + class: 'middleBorderWithShadow', + x: layoutInfoObs.map(layoutInfo => layoutInfo.code1.x - shadowWidth), + y: layoutInfoObs.map(layoutInfo => layoutInfo.code1.y), + width: shadowWidth, + height: layoutInfoObs.map(layoutInfo => layoutInfo.code2.y - layoutInfo.code1.y), + fill: 'url(#gradient)', + style: { strokeWidth: '0', stroke: 'transparent', } + }) + ] + ) + ]; + })).keepUpdated(this._store); + + private readonly _display = derived(this, reader => !!this._uiState.read(reader) ? 'block' : 'none'); + + private readonly _nonOverflowView = n.div({ + class: 'inline-edits-view', + style: { + position: 'absolute', + overflow: 'visible', + top: '0px', + left: '0px', + zIndex: '0', + display: this._display, + }, + }, [ + this._backgroundSvg, + derived(this, reader => this._shouldOverflow.read(reader) ? [] : [this._editorContainer, this._foregroundSvg]), + ]).keepUpdated(this._store); + + private readonly _overflowView = n.div({ + class: 'inline-edits-view', + style: { + position: 'absolute', + overflow: 'visible', + top: '0px', + left: '0px', + zIndex: '100', + display: this._display, + }, + }, [ + derived(this, reader => this._shouldOverflow.read(reader) ? [this._editorContainer, this._foregroundSvg] : []), + ]).keepUpdated(this._store); +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index bd79ca27bf7..e350ade4807 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { h } from '../../../../../../base/browser/dom.js'; +import { h, isSVGElement } from '../../../../../../base/browser/dom.js'; import { KeybindingLabel, unthemedKeybindingLabelOptions } from '../../../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; import { numberComparator } from '../../../../../../base/common/arrays.js'; import { findFirstMin } from '../../../../../../base/common/arraysFind.js'; -import { IReader } from '../../../../../../base/common/observable.js'; +import { derived, IObservable, IReader } from '../../../../../../base/common/observable.js'; import { OS } from '../../../../../../base/common/platform.js'; import { splitLines, getIndentationLength } from '../../../../../../base/common/strings.js'; import { URI } from '../../../../../../base/common/uri.js'; @@ -19,6 +19,8 @@ import { SingleTextEdit, TextEdit } from '../../../../../common/core/textEdit.js import { RangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { Range } from '../../../../../common/core/range.js'; import { Position } from '../../../../../common/core/position.js'; +import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; +import { BugIndicatingError } from '../../../../../../base/common/errors.js'; export function maxContentWidthInRange(editor: ObservableCodeEditor, range: LineRange, reader: IReader): number { editor.layoutInfo.read(reader); @@ -170,3 +172,267 @@ export class PathBuilder { return this._data; } } + +type Value = T | IObservable; +type ValueOrList = Value | Value[]; +type ValueOrList2 = ValueOrList | ValueOrList>; + +type Element = HTMLElement | SVGElement; + +type SVGElementTagNameMap2 = { + svg: SVGElement & { + width: number; + height: number; + transform: string; + }; + path: SVGElement & { + d: string; + }; + linearGradient: SVGElement & { + id: string; + x1: string | number; + x2: string | number; + }; + stop: SVGElement & { + offset: string; + }; + rect: SVGElement & { + x: number; + y: number; + width: number; + height: number; + fill: string; + }; + defs: SVGElement; +}; + +type DomTagCreateFn> = + ( + tag: TTag, + attributes: ElementAttributeKeys & { class?: Value; ref?: IRef }, + children?: ValueOrList2, + ) => ObserverNode; + +type DomCreateFn = + ( + attributes: ElementAttributeKeys & { class?: Value; ref?: IRef }, + children?: ValueOrList2, + ) => ObserverNode; + +export namespace n { + function nodeNs>(elementNs: string | undefined = undefined): DomTagCreateFn { + return (tag, attributes, children) => { + const className = attributes.class; + delete attributes.class; + const ref = attributes.ref; + delete attributes.ref; + + return new ObserverNodeWithElement(tag as any, ref, elementNs, className, attributes, children); + }; + } + + function node, TKey extends keyof TMap>(tag: TKey, elementNs: string | undefined = undefined): DomCreateFn { + const f = nodeNs(elementNs) as any; + return (attributes, children) => { + return f(tag, attributes, children); + }; + } + + export const div: DomCreateFn = node('div'); + export const svg: DomCreateFn = node('svg', 'http://www.w3.org/2000/svg'); + + export const svgElem = nodeNs('http://www.w3.org/2000/svg'); + + export function ref(): Ref { + return new Ref(); + } +} + +export interface IRef { + setValue(value: T): void; +} + +export class Ref implements IRef { + private _value: T | undefined = undefined; + + public setValue(value: T): void { + this._value = value; + } + + public get element(): T { + if (!this._value) { + throw new BugIndicatingError('Make sure the ref is set before accessing the element. Maybe wrong initialization order?'); + } + return this._value; + } +} + +export abstract class ObserverNode extends Disposable { + private readonly _deriveds: (IObservable)[] = []; + + protected readonly _element: T; + + constructor( + tag: string, + ref: IRef | undefined, + ns: string | undefined, + className: Value | undefined, + attributes: ElementAttributeKeys, + children: ValueOrList2 | undefined, + ) { + super(); + + this._element = (ns ? document.createElementNS(ns, tag) : document.createElement(tag)) as unknown as T; + if (ref) { + ref.setValue(this._element); + } + + function setClassName(domNode: Element, className: string | string[]) { + if (isSVGElement(domNode)) { + if (Array.isArray(className)) { + domNode.setAttribute('class', className.join(' ')); + } else { + domNode.setAttribute('class', className); + } + } else { + if (Array.isArray(className)) { + domNode.className = className.join(' '); + } else { + domNode.className = className; + } + } + } + + if (className) { + if (isObservable(className)) { + this._deriveds.push(derived(this, reader => { + setClassName(this._element, className.read(reader)); + })); + } else { + setClassName(this._element, className); + } + } + + function convertCssValue(value: any): string { + if (typeof value === 'number') { + return value + 'px'; + } + return value; + } + + for (const [key, value] of Object.entries(attributes)) { + if (key === 'style') { + for (const [cssKey, cssValue] of Object.entries(value)) { + const key = camelCaseToHyphenCase(cssKey); + if (isObservable(cssValue)) { + this._deriveds.push(derived(this, reader => { + this._element.style.setProperty(key, convertCssValue(cssValue.read(reader))); + })); + } else { + this._element.style.setProperty(key, convertCssValue(cssValue)); + } + } + } else if (key === 'tabIndex') { + if (isObservable(value)) { + this._deriveds.push(derived(this, reader => { + this._element.tabIndex = value.read(reader) as any; + })); + } else { + this._element.tabIndex = value; + } + } else { + if (isObservable(value)) { + this._deriveds.push(derived(this, reader => { + this._element.setAttribute(camelCaseToHyphenCase(key), value.read(reader) as any); + })); + } else { + this._element.setAttribute(camelCaseToHyphenCase(key), value.toString()); + } + } + } + + function getChildren(reader: IReader | undefined, children: ValueOrList2): (Element | string)[] { + if (isObservable(children)) { + return getChildren(reader, children.read(reader)); + } + if (Array.isArray(children)) { + return children.flatMap(c => getChildren(reader, c)); + } + if (children instanceof ObserverNode) { + if (reader) { + children.readEffect(reader); + } + return [children._element]; + } + if (children) { + return [children]; + } + return []; + } + + function childrenIsObservable(children: ValueOrList2): boolean { + if (isObservable(children)) { + return true; + } + if (Array.isArray(children)) { + return children.some(c => childrenIsObservable(c)); + } + return false; + } + + if (children) { + const d = derived(this, reader => { + this._element.replaceChildren(...getChildren(reader, children)); + }); + this._deriveds.push(d); + if (!childrenIsObservable(children)) { + d.get(); + } + } + } + + readEffect(reader: IReader | undefined): void { + for (const d of this._deriveds) { + d.read(reader); + } + } + + keepUpdated(store: DisposableStore): ObserverNodeWithElement { + derived(reader => { + this.readEffect(reader); + }).recomputeInitiallyAndOnChange(store); + return this as unknown as ObserverNodeWithElement; + } +} + +export class ObserverNodeWithElement extends ObserverNode { + public get element() { + return this._element; + } +} + +function camelCaseToHyphenCase(str: string) { + return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); +} + +function isObservable(obj: any): obj is IObservable { + return obj && typeof obj === 'object' && obj['read'] !== undefined && obj['reportChanges'] !== undefined; +} + +type ElementAttributeKeys = Partial<{ + [K in keyof T]: T[K] extends Function + ? never + : T[K] extends object + ? ElementAttributeKeys + : Value +}>; + +export function mapOutFalsy(obs: IObservable): IObservable | undefined | null | false> { + return derived(reader => { + const val = obs.read(reader); + if (!val) { + return undefined; + } + return obs as IObservable; + }); +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.css b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css similarity index 98% rename from src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.css rename to src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css index e4b742e9391..e5531dc7b4b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.css +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css @@ -131,8 +131,6 @@ .current-line-margin { border: none; } - - --vscode-editor-background: transparent; } } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts new file mode 100644 index 00000000000..391cf7516b7 --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { derived, IObservable } from '../../../../../../base/common/observable.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; +import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { EditorOption } from '../../../../../common/config/editorOptions.js'; +import { LineRange } from '../../../../../common/core/lineRange.js'; +import { StringText } from '../../../../../common/core/textEdit.js'; +import { DetailedLineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; +import { TextModel } from '../../../../../common/model/textModel.js'; +import './view.css'; +import { IOriginalEditorInlineDiffViewState, OriginalEditorInlineDiffView } from './inlineDiffView.js'; +import { applyEditToModifiedRangeMappings, createReindentEdit } from './utils.js'; +import { IInlineEditsIndicatorState, InlineEditsIndicator } from './indicatorView.js'; +import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import { InlineEditWithChanges } from './viewAndDiffProducer.js'; +import { InlineEditsSideBySideDiff } from './sideBySideDiff.js'; + +export class InlineEditsView extends Disposable { + private readonly _editorObs = observableCodeEditor(this._editor); + + private readonly _useMixedLinesDiff = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useMixedLinesDiff); + private readonly _useInterleavedLinesDiff = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useInterleavedLinesDiff); + + constructor( + private readonly _editor: ICodeEditor, + private readonly _edit: IObservable, + private readonly _model: IObservable, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + super(); + } + + private readonly _uiState = derived<{ + state: 'collapsed' | 'mixedLines' | 'interleavedLines' | 'sideBySide'; + diff: DetailedLineRangeMapping[]; + edit: InlineEditWithChanges; + newText: string; + newTextLineCount: number; + originalDisplayRange: LineRange; + } | undefined>(this, reader => { + const edit = this._edit.read(reader); + if (!edit) { + return undefined; + } + + this._model.get()?.handleInlineCompletionShown(edit.inlineCompletion); + + let mappings = RangeMapping.fromEdit(edit.edit); + let newText = edit.edit.apply(edit.originalText); + let diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); + + let state: 'collapsed' | 'mixedLines' | 'interleavedLines' | 'sideBySide'; + if (edit.isCollapsed) { + state = 'collapsed'; + } else if (diff.every(m => OriginalEditorInlineDiffView.supportsInlineDiffRendering(m)) && + (this._useMixedLinesDiff.read(reader) === 'whenPossible' || (edit.userJumpedToIt && this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible'))) { + state = 'mixedLines'; + } else if ((this._useInterleavedLinesDiff.read(reader) === 'always' || (edit.userJumpedToIt && this._useInterleavedLinesDiff.read(reader) === 'afterJump'))) { + state = 'interleavedLines'; + } else { + state = 'sideBySide'; + } + + if (state === 'sideBySide') { + const indentationAdjustmentEdit = createReindentEdit(newText, edit.modifiedLineRange); + newText = indentationAdjustmentEdit.applyToString(newText); + + mappings = applyEditToModifiedRangeMappings(mappings, indentationAdjustmentEdit); + diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); + } + + const originalDisplayRange = edit.originalText.lineRange.intersect( + edit.originalLineRange.join( + LineRange.ofLength(edit.originalLineRange.startLineNumber, edit.lineEdit.newLines.length) + ) + )!; + + this._previewTextModel.setLanguage(this._editor.getModel()!.getLanguageId()); + this._previewTextModel.setValue(newText); + + return { + state, + diff, + edit, + newText, + newTextLineCount: edit.modifiedLineRange.length, + originalDisplayRange: originalDisplayRange, + }; + }); + + private readonly _previewTextModel = this._register(this._instantiationService.createInstance( + TextModel, + '', + this._editor.getModel()!.getLanguageId(), + { ...TextModel.DEFAULT_CREATION_OPTIONS, bracketPairColorizationOptions: { enabled: true, independentColorPoolPerBracketType: false } }, + null + )); + + private readonly _sideBySide = this._register(this._instantiationService.createInstance(InlineEditsSideBySideDiff, + this._editor, + this._edit, + this._previewTextModel, + this._uiState.map(s => s && s.state === 'sideBySide' ? ({ + edit: s.edit, + newTextLineCount: s.newTextLineCount, + originalDisplayRange: s.originalDisplayRange, + }) : undefined), + )); + + private readonly _inlineDiffViewState = derived(this, reader => { + const e = this._uiState.read(reader); + if (!e) { return undefined; } + + return { + modifiedText: new StringText(e.newText), + diff: e.diff, + mode: e.state === 'collapsed' ? 'sideBySide' : e.state, + modifiedCodeEditor: this._sideBySide.previewEditor, + }; + }); + + protected readonly _inlineDiffView = this._register(new OriginalEditorInlineDiffView(this._editor, this._inlineDiffViewState, this._previewTextModel)); + + protected readonly _indicator = this._register(new InlineEditsIndicator( + this._editorObs, + derived(reader => { + const state = this._uiState.read(reader); + if (!state) { return undefined; } + + const range = state.originalDisplayRange; + const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); + + return { editTop: top, showAlways: state.state !== 'sideBySide' }; + }), + this._model, + )); +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewAndDiffProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts similarity index 99% rename from src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewAndDiffProducer.ts rename to src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts index a4e52c22af7..3f31d826857 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewAndDiffProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts @@ -23,7 +23,7 @@ import { IModelService } from '../../../../../common/services/model.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { InlineEdit } from '../../model/inlineEdit.js'; import { InlineCompletionItem } from '../../model/provideInlineCompletions.js'; -import { InlineEditsView } from './inlineEditsView.js'; +import { InlineEditsView } from './view.js'; import { UniqueUriGenerator } from './utils.js'; export class InlineEditsViewAndDiffProducer extends Disposable { From d3145c5087eeb8777b80cda7f38651e1242b2f81 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 12 Dec 2024 16:02:39 +0100 Subject: [PATCH 152/479] workspaceFolder variable substitution in launch.json or tasks.json should use URI for virtual filesystems (#235954) Fixes #197377 --- .../api/common/extHostVariableResolverService.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostVariableResolverService.ts b/src/vs/workbench/api/common/extHostVariableResolverService.ts index 1acc6ab696f..47345d32b75 100644 --- a/src/vs/workbench/api/common/extHostVariableResolverService.ts +++ b/src/vs/workbench/api/common/extHostVariableResolverService.ts @@ -18,6 +18,7 @@ import { IConfigurationResolverService } from '../../services/configurationResol import { AbstractVariableResolverService } from '../../services/configurationResolver/common/variableResolver.js'; import * as vscode from 'vscode'; import { ExtHostConfigProvider, IExtHostConfiguration } from './extHostConfiguration.js'; +import { Schemas } from '../../../base/common/network.js'; export interface IExtHostVariableResolverProvider { readonly _serviceBrand: undefined; @@ -83,7 +84,11 @@ class ExtHostVariableResolverService extends AbstractVariableResolverService { getFilePath: (): string | undefined => { const activeUri = getActiveUri(); if (activeUri) { - return path.normalize(activeUri.fsPath); + if (activeUri.scheme === Schemas.file) { + return path.normalize(activeUri.fsPath); + } else { + return activeUri.toString(); + } } return undefined; }, @@ -93,7 +98,11 @@ class ExtHostVariableResolverService extends AbstractVariableResolverService { if (activeUri) { const ws = workspaceService.getWorkspaceFolder(activeUri); if (ws) { - return path.normalize(ws.uri.fsPath); + if (activeUri.scheme === Schemas.file) { + return path.normalize(ws.uri.fsPath); + } else { + return ws.uri.toString(); + } } } } From 2de8e8ec77e13266c4f9e25abe73a2775ea27939 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 12 Dec 2024 16:31:21 +0100 Subject: [PATCH 153/479] fix https://github.com/microsoft/vscode/issues/216924 (#235956) --- .../contrib/gotoError/browser/markerNavigationService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts index 61b92a3e865..bb3be6eb6ac 100644 --- a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts +++ b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts @@ -137,6 +137,9 @@ export class MarkerList { if (!found) { // after the last change this._nextIdx = fwd ? 0 : this._markers.length - 1; + } else if (found && !fwd) { + // we went past and have to go one back + this._nextIdx -= 1; } if (this._nextIdx < 0) { this._nextIdx = this._markers.length - 1; From ded87f9d8e5b59a4a0bc47745947c5154ad207f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 12 Dec 2024 16:46:14 +0100 Subject: [PATCH 154/479] update unique names generator (#235958) --- extensions/git/package-lock.json | 9 +++++---- extensions/git/package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index b15b708e618..cf05db079db 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@joaomoreno/unique-names-generator": "^5.1.0", + "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.0", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", @@ -31,9 +31,10 @@ } }, "node_modules/@joaomoreno/unique-names-generator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@joaomoreno/unique-names-generator/-/unique-names-generator-5.1.0.tgz", - "integrity": "sha512-KEVThTpUIKPb7dBKJ9mJ3WYnD1mJZZsEinCSp9CVEPlWbDagurFv1RKRjvvujrLfJzsGc0HkBHS9W8Bughao4A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@joaomoreno/unique-names-generator/-/unique-names-generator-5.2.0.tgz", + "integrity": "sha512-JEh3qZ85Z6syFvQlhRGRyTPI1M5VticiiP8Xl8EV0XfyfI4Mwzd6Zw28BBrEgUJCYv/cpKCQClVj3J8Tn0KFiA==", + "license": "MIT", "engines": { "node": ">=8" } diff --git a/extensions/git/package.json b/extensions/git/package.json index 5e7caad22bb..81c825251a4 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3470,7 +3470,7 @@ ] }, "dependencies": { - "@joaomoreno/unique-names-generator": "^5.1.0", + "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.0", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", From 4920694eceb2bf01b0e2316b0fbd2b965947d7e3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 12 Dec 2024 16:48:37 +0100 Subject: [PATCH 155/479] fix #234843 (#235955) * fix #234843 * fix lint error --- cli/src/bin/code/legacy_args.rs | 2 ++ cli/src/commands/args.rs | 7 +++++++ cli/src/tunnels/code_server.rs | 1 + src/vs/code/node/cliProcessMain.ts | 2 +- src/vs/platform/environment/common/argv.ts | 1 + src/vs/platform/environment/node/argv.ts | 1 + src/vs/server/node/remoteExtensionHostAgentCli.ts | 2 +- src/vs/server/node/server.cli.ts | 1 + src/vs/server/node/serverEnvironmentService.ts | 3 +++ 9 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cli/src/bin/code/legacy_args.rs b/cli/src/bin/code/legacy_args.rs index 0bd92c92fd3..b61611a8057 100644 --- a/cli/src/bin/code/legacy_args.rs +++ b/cli/src/bin/code/legacy_args.rs @@ -85,6 +85,8 @@ pub fn try_parse_legacy( subcommand: ExtensionSubcommand::Install(InstallExtensionArgs { id_or_path: exts, pre_release: args.contains_key("pre-release"), + donot_include_pack_and_dependencies: args + .contains_key("do-not-include-pack-dependencies"), force: args.contains_key("force"), }), desktop_code_options, diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index 21efc1c6667..b9f7b3b7875 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -293,6 +293,9 @@ impl ExtensionSubcommand { if args.pre_release { target.push("--pre-release".to_string()); } + if args.donot_include_pack_and_dependencies { + target.push("do-not-include-pack-dependencies".to_string()); + } if args.force { target.push("--force".to_string()); } @@ -333,6 +336,10 @@ pub struct InstallExtensionArgs { #[clap(long)] pub pre_release: bool, + /// Don't include installing pack and dependencies of the extension + #[clap(long)] + pub donot_include_pack_and_dependencies: bool, + /// Update to the latest version of the extension if it's already installed. #[clap(long)] pub force: bool, diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index d7d91a83c55..cf00bc42835 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -67,6 +67,7 @@ pub struct CodeServerArgs { pub show_versions: bool, pub category: Option, pub pre_release: bool, + pub donot_include_pack_and_dependencies: bool, pub force: bool, pub start_server: bool, // connection tokens diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index decb23f830f..e6f25b20b53 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -286,7 +286,7 @@ class CliMain extends Disposable { // Install Extension else if (this.argv['install-extension'] || this.argv['install-builtin-extension']) { - const installOptions: InstallOptions = { isMachineScoped: !!this.argv['do-not-sync'], installPreReleaseVersion: !!this.argv['pre-release'], profileLocation }; + const installOptions: InstallOptions = { isMachineScoped: !!this.argv['do-not-sync'], installPreReleaseVersion: !!this.argv['pre-release'], donotIncludePackAndDependencies: !!this.argv['do-not-include-pack-dependencies'], profileLocation }; return instantiationService.createInstance(ExtensionManagementCLI, new ConsoleLogger(LogLevel.Info, false)).installExtensions(this.asExtensionIdOrVSIX(this.argv['install-extension'] || []), this.asExtensionIdOrVSIX(this.argv['install-builtin-extension'] || []), installOptions, !!this.argv['force']); } diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 818021ff0c1..87825ee7d2c 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -82,6 +82,7 @@ export interface NativeParsedArgs { 'install-builtin-extension'?: string[]; // undefined or array of 1 or more 'uninstall-extension'?: string[]; // undefined or array of 1 or more 'update-extensions'?: boolean; + 'do-not-include-pack-dependencies'?: boolean; 'locate-extension'?: string[]; // undefined or array of 1 or more 'enable-proposed-api'?: string[]; // undefined or array of 1 or more 'open-url'?: boolean; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 379e327c646..4ef107c79bc 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -164,6 +164,7 @@ export const OPTIONS: OptionDescriptions> = { 'install-builtin-extension': { type: 'string[]' }, 'force': { type: 'boolean' }, 'do-not-sync': { type: 'boolean' }, + 'do-not-include-pack-dependencies': { type: 'boolean' }, 'trace': { type: 'boolean' }, 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index cd1fca04f58..87ceb0788c9 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -152,7 +152,7 @@ class CliMain extends Disposable { // Install Extension else if (this.args['install-extension'] || this.args['install-builtin-extension']) { - const installOptions: InstallOptions = { isMachineScoped: !!this.args['do-not-sync'], installPreReleaseVersion: !!this.args['pre-release'] }; + const installOptions: InstallOptions = { isMachineScoped: !!this.args['do-not-sync'], installPreReleaseVersion: !!this.args['pre-release'], donotIncludePackAndDependencies: !!this.args['do-not-include-pack-dependencies'] }; return extensionManagementCLI.installExtensions(this.asExtensionIdOrVSIX(this.args['install-extension'] || []), this.asExtensionIdOrVSIX(this.args['install-builtin-extension'] || []), installOptions, !!this.args['force']); } diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index 654a769cd27..324a92bf937 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -71,6 +71,7 @@ const isSupportedForPipe = (optionId: keyof RemoteParsedArgs) => { case 'update-extensions': case 'list-extensions': case 'force': + case 'do-not-include-pack-dependencies': case 'show-versions': case 'category': case 'verbose': diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index 2770df603e5..b3e97edc704 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -69,6 +69,7 @@ export const serverOptions: OptionDescriptions> = { 'category': OPTIONS['category'], 'force': OPTIONS['force'], 'do-not-sync': OPTIONS['do-not-sync'], + 'do-not-include-pack-dependencies': OPTIONS['do-not-include-pack-dependencies'], 'pre-release': OPTIONS['pre-release'], 'start-server': { type: 'boolean', cat: 'e', description: nls.localize('start-server', "Start the server when installing or uninstalling extensions. To be used in combination with 'install-extension', 'install-builtin-extension' and 'uninstall-extension'.") }, @@ -194,6 +195,8 @@ export interface ServerParsedArgs { force?: boolean; // used by install-extension 'do-not-sync'?: boolean; // used by install-extension 'pre-release'?: boolean; // used by install-extension + 'do-not-include-pack-dependencies'?: boolean; // used by install-extension + 'start-server'?: boolean; From 69e1fe0d55d416a8ce2c30d13ba7b120c526de8a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 12 Dec 2024 16:54:22 +0100 Subject: [PATCH 156/479] fix #227446 - timeout request after 10s (#235960) --- .../extensionManagement/common/extensionGalleryService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 38e689b9b88..9a9157545ce 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -1516,7 +1516,12 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi return { malicious: [], deprecated: {}, search: [] }; } - const context = await this.requestService.request({ type: 'GET', url: this.extensionsControlUrl }, CancellationToken.None); + const context = await this.requestService.request({ + type: 'GET', + url: this.extensionsControlUrl, + timeout: 10000 /*10s*/ + }, CancellationToken.None); + if (context.res.statusCode !== 200) { throw new Error('Could not get extensions report.'); } From 4182e343f05580cb8230e9e0fe180ec2f16804e0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 12 Dec 2024 17:11:37 +0100 Subject: [PATCH 157/479] polish: adding dot (#235962) --- 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 3fca8a3fc7b..8bd08d2ee1a 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -5703,7 +5703,7 @@ export const EditorOptions = { nls.localize('editor.colorDecoratorActivatedOn.hover', "Make the color picker appear on hover of the color decorator"), nls.localize('editor.colorDecoratorActivatedOn.click', "Make the color picker appear on click of the color decorator") ], - description: nls.localize('colorDecoratorActivatedOn', "Controls the condition to make a color picker appear from a color decorator") + description: nls.localize('colorDecoratorActivatedOn', "Controls the condition to make a color picker appear from a color decorator.") })), colorDecoratorsLimit: register(new EditorIntOption( EditorOption.colorDecoratorsLimit, 'colorDecoratorsLimit', 500, 1, 1000000, From eb79f015555c391313d120013bf0b01feaec84d4 Mon Sep 17 00:00:00 2001 From: leopardracer <136604165+leopardracer@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:17:36 +0200 Subject: [PATCH 158/479] Update snippetSession.ts --- src/vs/editor/contrib/snippet/browser/snippetSession.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/snippet/browser/snippetSession.ts b/src/vs/editor/contrib/snippet/browser/snippetSession.ts index 34bf99e7d4a..79cf57b3abf 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.ts @@ -471,7 +471,7 @@ export class SnippetSession { const readClipboardText = () => clipboardText; // know what text the overwrite[Before|After] extensions - // of the primary curser have selected because only when + // of the primary cursor have selected because only when // secondary selections extend to the same text we can grow them const firstBeforeText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), overwriteBefore, 0)); const firstAfterText = model.getValueInRange(SnippetSession.adjustSelection(model, editor.getSelection(), 0, overwriteAfter)); @@ -530,7 +530,7 @@ export class SnippetSession { ])); // store snippets with the index of their originating selection. - // that ensures the primiary cursor stays primary despite not being + // that ensures the primary cursor stays primary despite not being // the one with lowest start position edits[idx] = EditOperation.replace(snippetSelection, snippet.toString()); edits[idx].identifier = { major: idx, minor: 0 }; // mark the edit so only our undo edits will be used to generate end cursors From 7b9ca91c54d7f364cc7bac35cfb4fd5c86ee0026 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 12 Dec 2024 17:43:34 +0100 Subject: [PATCH 159/479] Focus on editor after sticky scroll disabled (#235967) focus on editor --- .../stickyScroll/browser/stickyScrollActions.ts | 16 ++++++++++------ .../browser/stickyScrollController.ts | 5 +++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts index 5065cd8667f..ce50821ebf7 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollActions.ts @@ -7,7 +7,7 @@ import { KeyCode } from '../../../../base/common/keyCodes.js'; import { EditorAction2, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { Action2, MenuId } from '../../../../platform/actions/common/actions.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; @@ -15,7 +15,7 @@ import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { StickyScrollController } from './stickyScrollController.js'; -export class ToggleStickyScroll extends Action2 { +export class ToggleStickyScroll extends EditorAction2 { constructor() { super({ @@ -41,10 +41,14 @@ export class ToggleStickyScroll extends Action2 { }); } - override async run(accessor: ServicesAccessor): Promise { + async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const configurationService = accessor.get(IConfigurationService); const newValue = !configurationService.getValue('editor.stickyScroll.enabled'); - return configurationService.updateValue('editor.stickyScroll.enabled', newValue); + const isFocused = StickyScrollController.get(editor)?.isFocused(); + configurationService.updateValue('editor.stickyScroll.enabled', newValue); + if (isFocused) { + editor.focus(); + } } } @@ -56,8 +60,8 @@ export class FocusStickyScroll extends EditorAction2 { super({ id: 'editor.action.focusStickyScroll', title: { - ...localize2('focusStickyScroll', "Focus on the editor sticky scroll"), - mnemonicTitle: localize({ key: 'mifocusStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Focus Sticky Scroll"), + ...localize2('focusStickyScroll', "Focus Editor Sticky Scroll"), + mnemonicTitle: localize({ key: 'mifocusEditorStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Focus Editor Sticky Scroll"), }, precondition: ContextKeyExpr.and(ContextKeyExpr.has('config.editor.stickyScroll.enabled'), EditorContextKeys.stickyScrollVisible), menu: [ diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 4f94c670552..b5074c87786 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -33,6 +33,7 @@ import { FoldingModel, toggleCollapseState } from '../../folding/browser/folding export interface IStickyScrollController { get stickyScrollCandidateProvider(): IStickyLineCandidateProvider; get stickyScrollWidgetState(): StickyScrollWidgetState; + isFocused(): boolean; focus(): void; focusNext(): void; focusPrevious(): void; @@ -141,6 +142,10 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._onMouseDown = false; } + public isFocused(): boolean { + return this._focused; + } + public focus(): void { // If the mouse is down, do not focus on the sticky scroll if (this._onMouseDown) { From de13422f41e460a16c8b466283b30a4519f6f7df Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 12 Dec 2024 18:09:09 +0100 Subject: [PATCH 160/479] fix https://github.com/microsoft/vscode/issues/235965 and some modernising (#235975) --- .../contrib/gotoError/browser/gotoError.ts | 66 ++++++++------ .../browser/markerNavigationService.ts | 91 ++++++++++++------- 2 files changed, 95 insertions(+), 62 deletions(-) diff --git a/src/vs/editor/contrib/gotoError/browser/gotoError.ts b/src/vs/editor/contrib/gotoError/browser/gotoError.ts index 6c121121be5..eea7aa4931a 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoError.ts @@ -128,40 +128,46 @@ export class MarkerController implements IEditorContribution { } showAtMarker(marker: IMarker): void { - if (this._editor.hasModel()) { - const model = this._getOrCreateModel(this._editor.getModel().uri); - model.resetIndex(); - model.move(true, this._editor.getModel(), new Position(marker.startLineNumber, marker.startColumn)); - if (model.selected) { - this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total); - } + if (!this._editor.hasModel()) { + return; + } + + const textModel = this._editor.getModel(); + const model = this._getOrCreateModel(textModel.uri); + model.resetIndex(); + model.move(true, textModel, new Position(marker.startLineNumber, marker.startColumn)); + if (model.selected) { + this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total); } } - async nagivate(next: boolean, multiFile: boolean) { - if (this._editor.hasModel()) { - const model = this._getOrCreateModel(multiFile ? undefined : this._editor.getModel().uri); - model.move(next, this._editor.getModel(), this._editor.getPosition()); - if (!model.selected) { - return; - } - if (model.selected.marker.resource.toString() !== this._editor.getModel().uri.toString()) { - // show in different editor - this._cleanUp(); - const otherEditor = await this._editorService.openCodeEditor({ - resource: model.selected.marker.resource, - options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.NearTop, selection: model.selected.marker } - }, this._editor); - - if (otherEditor) { - MarkerController.get(otherEditor)?.close(); - MarkerController.get(otherEditor)?.nagivate(next, multiFile); - } + async navigate(next: boolean, multiFile: boolean) { + if (!this._editor.hasModel()) { + return; + } - } else { - // show in this editor - this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total); + const textModel = this._editor.getModel(); + const model = this._getOrCreateModel(multiFile ? undefined : textModel.uri); + model.move(next, textModel, this._editor.getPosition()); + if (!model.selected) { + return; + } + if (model.selected.marker.resource.toString() !== textModel.uri.toString()) { + // show in different editor + this._cleanUp(); + const otherEditor = await this._editorService.openCodeEditor({ + resource: model.selected.marker.resource, + options: { pinned: false, revealIfOpened: true, selectionRevealType: TextEditorSelectionRevealType.NearTop, selection: model.selected.marker } + }, this._editor); + + if (otherEditor) { + MarkerController.get(otherEditor)?.close(); + MarkerController.get(otherEditor)?.navigate(next, multiFile); } + + } else { + // show in this editor + this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total); } } } @@ -178,7 +184,7 @@ class MarkerNavigationAction extends EditorAction { async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { if (editor.hasModel()) { - MarkerController.get(editor)?.nagivate(this._next, this._multiFile); + await MarkerController.get(editor)?.navigate(this._next, this._multiFile); } } } diff --git a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts index bb3be6eb6ac..bc02d0a62fa 100644 --- a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts +++ b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { binarySearch } from '../../../../base/common/arrays.js'; +import { binarySearch2, equals } from '../../../../base/common/arrays.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { LinkedList } from '../../../../base/common/linkedList.js'; @@ -16,6 +16,7 @@ import { InstantiationType, registerSingleton } from '../../../../platform/insta import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IMarker, IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { isEqual } from '../../../../base/common/resources.js'; export class MarkerCoordinate { constructor( @@ -61,23 +62,39 @@ export class MarkerList { }; const updateMarker = () => { - this._markers = this._markerService.read({ + let newMarkers = this._markerService.read({ resource: URI.isUri(resourceFilter) ? resourceFilter : undefined, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); if (typeof resourceFilter === 'function') { - this._markers = this._markers.filter(m => this._resourceFilter!(m.resource)); + newMarkers = newMarkers.filter(m => this._resourceFilter!(m.resource)); } - this._markers.sort(compareMarker); + newMarkers.sort(compareMarker); + + if (equals(newMarkers, this._markers, (a, b) => + a.resource.toString() === b.resource.toString() + && a.startLineNumber === b.startLineNumber + && a.startColumn === b.startColumn + && a.endLineNumber === b.endLineNumber + && a.endColumn === b.endColumn + && a.severity === b.severity + && a.message === b.message + )) { + return false; + } + + this._markers = newMarkers; + return true; }; updateMarker(); this._dispoables.add(_markerService.onMarkerChanged(uris => { if (!this._resourceFilter || uris.some(uri => this._resourceFilter!(uri))) { - updateMarker(); - this._nextIdx = -1; - this._onDidChange.fire(); + if (updateMarker()) { + this._nextIdx = -1; + this._onDidChange.fire(); + } } })); } @@ -103,44 +120,54 @@ export class MarkerList { } private _initIdx(model: ITextModel, position: Position, fwd: boolean): void { - let found = false; - let idx = this._markers.findIndex(marker => marker.resource.toString() === model.uri.toString()); + let idx = this._markers.findIndex(marker => isEqual(marker.resource, model.uri)); if (idx < 0) { - idx = binarySearch(this._markers, { resource: model.uri }, (a, b) => compare(a.resource.toString(), b.resource.toString())); + // ignore model, position because this will be a different file + idx = binarySearch2(this._markers.length, idx => compare(this._markers[idx].resource.toString(), model.uri.toString())); if (idx < 0) { idx = ~idx; } - } - - for (let i = idx; i < this._markers.length; i++) { - let range = Range.lift(this._markers[i]); + if (fwd) { + this._nextIdx = idx; + } else { + this._nextIdx = (this._markers.length + idx - 1) % this._markers.length; + } + } else { + // find marker for file + let found = false; + let wentPast = false; + for (let i = idx; i < this._markers.length; i++) { + let range = Range.lift(this._markers[i]); + + if (range.isEmpty()) { + const word = model.getWordAtPosition(range.getStartPosition()); + if (word) { + range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn); + } + } - if (range.isEmpty()) { - const word = model.getWordAtPosition(range.getStartPosition()); - if (word) { - range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn); + if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) { + this._nextIdx = i; + found = true; + wentPast = !range.containsPosition(position); + break; } - } - if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) { - this._nextIdx = i; - found = true; - break; + if (this._markers[i].resource.toString() !== model.uri.toString()) { + break; + } } - if (this._markers[i].resource.toString() !== model.uri.toString()) { - break; + if (!found) { + // after the last change + this._nextIdx = fwd ? 0 : this._markers.length - 1; + } else if (wentPast && !fwd) { + // we went past and have to go one back + this._nextIdx -= 1; } } - if (!found) { - // after the last change - this._nextIdx = fwd ? 0 : this._markers.length - 1; - } else if (found && !fwd) { - // we went past and have to go one back - this._nextIdx -= 1; - } if (this._nextIdx < 0) { this._nextIdx = this._markers.length - 1; } From 1ee0f190a18b334f8d6b339127e6321cd367bd72 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Thu, 12 Dec 2024 09:16:36 -0800 Subject: [PATCH 161/479] Update walkthrough step (#235865) --- .../common/gettingStartedContent.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 637e77f71f8..303905c9136 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -211,15 +211,14 @@ export const startEntries: GettingStartedStartEntryContent = [ const Button = (title: string, href: string) => `[${title}](${href})`; const CopilotStepTitle = localize('gettingStarted.copilotSetup.title', "Use AI features with Copilot for free"); -const CopilotDescription = localize({ key: 'gettingStarted.copilotSetup.description', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "Write code faster and smarter using [Copilot]({0}) for free with your GitHub account.", product.defaultChatAgent?.documentationUrl ?? ''); -const CopilotTermsString = localize({ key: 'copilotTerms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to Copilot [Terms]({0}) and [Privacy Policy]({1}).", product.defaultChatAgent?.termsStatementUrl ?? '', product.defaultChatAgent?.privacyStatementUrl ?? ''); -const CopilotSignedOutButton = Button(localize('setupCopilotButton.signIn', "Sign in to Use Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); -const CopilotSignedInButton = Button(localize('setupCopilotButton.setup', "Set Up Copilot"), `command:workbench.action.chat.triggerSetup?${encodeURIComponent(JSON.stringify([true]))}`); +const CopilotDescription = localize({ key: 'gettingStarted.copilotSetup.description', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "You can use [Copilot]({0}) to generate code across multiple files, fix errors, ask questions about your code and much more using natural language.", product.defaultChatAgent?.documentationUrl ?? ''); +const CopilotSignedOutButton = Button(localize('setupCopilotButton.signIn', "Set Up Copilot for Free"), `command:workbench.action.chat.triggerSetup`); +const CopilotSignedInButton = Button(localize('setupCopilotButton.setup', "Set Up Copilot for Free"), `command:workbench.action.chat.triggerSetup`); const CopilotCompleteButton = Button(localize('setupCopilotButton.chatWithCopilot', "Chat with Copilot"), 'command:workbench.action.chat.open'); function createCopilotSetupStep(id: string, button: string, when: string, includeTerms: boolean): BuiltinGettingStartedStep { const description = includeTerms ? - `${CopilotDescription}\n\n${CopilotTermsString}\n${button}` : + `${CopilotDescription}\n\n${button}` : `${CopilotDescription}\n${button}`; return { From 6a0fcdeca6cf8c4001b3ca822869e4817f496946 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 12 Dec 2024 09:17:26 -0800 Subject: [PATCH 162/479] Make `preparePasteEdits` interrupt diagnostics too For #235959 Already done for the main paste request but we should do it for `preparePasteEdits` too --- .../src/languageFeatures/copyPaste.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts index 898e007bdb9..d253e8f9ac9 100644 --- a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts +++ b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts @@ -60,10 +60,10 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { return; } - const response = await this._client.execute('preparePasteEdits', { + const response = await this._client.interruptGetErr(() => this._client.execute('preparePasteEdits', { file, copiedTextSpan: ranges.map(typeConverters.Range.toTextSpan), - }, token); + }, token)); if (token.isCancellationRequested || response.type !== 'response' || !response.body) { return; } From cd9144314e19156b99ba022a8d6b695764b8a75f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 12 Dec 2024 18:19:36 +0100 Subject: [PATCH 163/479] fix #172251 (#235978) --- .../extensions/browser/extensionEditor.ts | 20 +++++++++---------- .../browser/media/extensionEditor.css | 5 +++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 749790dd3a8..e73432b6040 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -1014,19 +1014,19 @@ export class ExtensionEditor extends EditorPane { const installInfo = append(installInfoContainer, $('.more-info')); append(installInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('id', "Identifier")), + $('div.more-info-entry-name', undefined, localize('id', "Identifier")), $('code', undefined, extension.identifier.id) )); append(installInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('Version', "Version")), + $('div.more-info-entry-name', undefined, localize('Version', "Version")), $('code', undefined, extension.manifest.version) ) ); if (extension.installedTimestamp) { append(installInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('last updated', "Last Updated")), + $('div.more-info-entry-name', undefined, localize('last updated', "Last Updated")), $('div', undefined, toDateString(new Date(extension.installedTimestamp))) ) ); @@ -1035,7 +1035,7 @@ export class ExtensionEditor extends EditorPane { const element = $('div', undefined, extension.source === 'vsix' ? localize('vsix', "VSIX") : localize('other', "Local")); append(installInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('source', "Source")), + $('div.more-info-entry-name', undefined, localize('source', "Source")), element ) ); @@ -1049,7 +1049,7 @@ export class ExtensionEditor extends EditorPane { const element = $('div', undefined, toMemoryString(extension.size)); append(installInfo, $('.more-info-entry', undefined, - $('div', { title: localize('size when installed', "Size when installed") }, localize('size', "Size")), + $('div.more-info-entry-name', { title: localize('size when installed', "Size when installed") }, localize('size', "Size")), element ) ); @@ -1070,7 +1070,7 @@ export class ExtensionEditor extends EditorPane { const element = $('div', undefined, toMemoryString(cacheSize)); append(installInfo, $('.more-info-entry', undefined, - $('div', { title: localize('disk space used', "Cache size") }, localize('cache size', "Cache")), + $('div.more-info-entry-name', { title: localize('disk space used', "Cache size") }, localize('cache size', "Cache")), element) ); if (isNative && extension.location.scheme === Schemas.file) { @@ -1103,23 +1103,23 @@ export class ExtensionEditor extends EditorPane { if (!extension.local) { append(moreInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('id', "Identifier")), + $('div.more-info-entry-name', undefined, localize('id', "Identifier")), $('code', undefined, extension.identifier.id) )); append(moreInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('Version', "Version")), + $('div.more-info-entry-name', undefined, localize('Version', "Version")), $('code', undefined, gallery.version) ) ); } append(moreInfo, $('.more-info-entry', undefined, - $('div', undefined, localize('published', "Published")), + $('div.more-info-entry-name', undefined, localize('published', "Published")), $('div', undefined, toDateString(new Date(gallery.releaseDate))) ), $('.more-info-entry', undefined, - $('div', undefined, localize('last released', "Last Released")), + $('div.more-info-entry-name', undefined, localize('last released', "Last Released")), $('div', undefined, toDateString(new Date(gallery.lastUpdated))) ) ); diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 1e1067588d8..49f5a412793 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -312,6 +312,11 @@ cursor: pointer; } +.extension-editor > .body > .content > .details > .additional-details-container .more-info-container > .more-info > .more-info-entry > .more-info-entry-name { + overflow: hidden; + text-overflow: ellipsis; +} + .extension-editor > .body > .content > .details > .additional-details-container .more-info-container > .more-info > .more-info-entry > .link, .extension-editor > .header > .details > .pre-release-text a, .extension-editor > .header > .details > .actions-status-container > .status a { From 43d31daf41afdbde9b86806327a1bf512931e9b8 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 12 Dec 2024 18:24:19 +0100 Subject: [PATCH 164/479] inline completions debt from review of PR (#235980) debt from review --- .../editor/contrib/inlineCompletions/browser/utils.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts index 96706c5e3d5..0f37508a9a2 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts @@ -7,10 +7,10 @@ import { Permutation, compareBy } from '../../../../base/common/arrays.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { IObservable, observableValue, ISettableObservable, autorun, transaction, IReader } from '../../../../base/common/observable.js'; -import { splitLinesIncludeSeparators } from '../../../../base/common/strings.js'; import { ContextKeyValue, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; 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'; @@ -48,12 +48,9 @@ export function subtractPositions(pos1: Position, pos2: Position): Position { } export function substringPos(text: string, pos: Position): string { - let subtext = ''; - const lines = splitLinesIncludeSeparators(text); - for (let i = pos.lineNumber - 1; i < lines.length; i++) { - subtext += lines[i].substring(i === pos.lineNumber - 1 ? pos.column - 1 : 0); - } - return subtext; + const transformer = new PositionOffsetTransformer(text); + const offset = transformer.getOffset(pos); + return text.substring(offset); } export function getEndPositionsAfterApplying(edits: readonly SingleTextEdit[]): Position[] { From a897802b2d6c2da2bb012b3ab5b3db493ff9e63d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 12 Dec 2024 18:26:39 +0100 Subject: [PATCH 165/479] Adresses #235977 (#235981) --- .../browser/controller/inlineCompletionsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 41499ed6f9f..6be0ecd1c90 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -146,7 +146,7 @@ export class InlineCompletionsController extends Disposable { } const m = this.model.get(); if (!m) { return; } - if (m.inlineCompletionState.get()?.primaryGhostText) { + if (m.state.get()?.kind === 'ghostText') { this.model.get()?.stop(); } else if (m.state.get()?.inlineCompletion) { this.model.get()?.collapseInlineEdit(); From 8c7ca99740995aa43301fe50eccee6bd5b676661 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 12 Dec 2024 09:36:15 -0800 Subject: [PATCH 166/479] Make sure we sync file config before generating paste edits This could explain why we occasionally see the wrong style of import being generated --- .../src/languageFeatures/copyPaste.ts | 30 +++++++++++-------- .../src/languageFeatures/formatting.ts | 6 ++-- .../src/languageProvider.ts | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts index 898e007bdb9..806573db323 100644 --- a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts +++ b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts @@ -5,12 +5,13 @@ import * as vscode from 'vscode'; import { DocumentSelector } from '../configuration/documentSelector'; +import { LanguageDescription } from '../configuration/languageDescription'; +import { API } from '../tsServer/api'; +import protocol from '../tsServer/protocol/protocol'; import * as typeConverters from '../typeConverters'; import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService'; +import FileConfigurationManager from './fileConfigurationManager'; import { conditionalRegistration, requireGlobalConfiguration, requireMinVersion, requireSomeCapability } from './util/dependentRegistration'; -import protocol from '../tsServer/protocol/protocol'; -import { API } from '../tsServer/api'; -import { LanguageDescription } from '../configuration/languageDescription'; class CopyMetadata { constructor( @@ -48,6 +49,7 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { constructor( private readonly _modeId: string, private readonly _client: ITypeScriptServiceClient, + private readonly fileConfigurationManager: FileConfigurationManager, ) { } async prepareDocumentPaste(document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken) { @@ -115,13 +117,17 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { return; } - const response = await this._client.interruptGetErr(() => this._client.execute('getPasteEdits', { - file, - // TODO: only supports a single paste for now - pastedText: [text], - pasteLocations: ranges.map(typeConverters.Range.toTextSpan), - copiedFrom - }, token)); + const response = await this._client.interruptGetErr(() => { + this.fileConfigurationManager.ensureConfigurationForDocument(document, token); + + return this._client.execute('getPasteEdits', { + file, + // TODO: only supports a single paste for now + pastedText: [text], + pasteLocations: ranges.map(typeConverters.Range.toTextSpan), + copiedFrom + }, token); + }); if (response.type !== 'response' || !response.body?.edits.length || token.isCancellationRequested) { return; } @@ -152,13 +158,13 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { } } -export function register(selector: DocumentSelector, language: LanguageDescription, client: ITypeScriptServiceClient) { +export function register(selector: DocumentSelector, language: LanguageDescription, client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager) { return conditionalRegistration([ requireSomeCapability(client, ClientCapability.Semantic), requireMinVersion(client, API.v570), requireGlobalConfiguration(language.id, enabledSettingId), ], () => { - return vscode.languages.registerDocumentPasteEditProvider(selector.semantic, new DocumentPasteProvider(language.id, client), { + return vscode.languages.registerDocumentPasteEditProvider(selector.semantic, new DocumentPasteProvider(language.id, client, fileConfigurationManager), { providedPasteEditKinds: [DocumentPasteProvider.kind], copyMimeTypes: [DocumentPasteProvider.metadataMimeType], pasteMimeTypes: [DocumentPasteProvider.metadataMimeType], diff --git a/extensions/typescript-language-features/src/languageFeatures/formatting.ts b/extensions/typescript-language-features/src/languageFeatures/formatting.ts index 3b31499c37b..575487b502d 100644 --- a/extensions/typescript-language-features/src/languageFeatures/formatting.ts +++ b/extensions/typescript-language-features/src/languageFeatures/formatting.ts @@ -15,7 +15,7 @@ import { conditionalRegistration, requireGlobalConfiguration } from './util/depe class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEditProvider, vscode.OnTypeFormattingEditProvider { public constructor( private readonly client: ITypeScriptServiceClient, - private readonly formattingOptionsManager: FileConfigurationManager + private readonly fileConfigurationManager: FileConfigurationManager ) { } public async provideDocumentRangeFormattingEdits( @@ -29,7 +29,7 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit return undefined; } - await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); + await this.fileConfigurationManager.ensureConfigurationOptions(document, options, token); const args = typeConverters.Range.toFormattingRequestArgs(file, range); const response = await this.client.execute('format', args, token); @@ -52,7 +52,7 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit return []; } - await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); + await this.fileConfigurationManager.ensureConfigurationOptions(document, options, token); const args: Proto.FormatOnKeyRequestArgs = { ...typeConverters.Position.toFileLocationRequestArgs(file, position), diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 7b95591604b..feb47f09683 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -65,7 +65,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/codeLens/implementationsCodeLens').then(provider => this._register(provider.register(selector, this.description, this.client, cachedNavTreeResponse))), import('./languageFeatures/codeLens/referencesCodeLens').then(provider => this._register(provider.register(selector, this.description, this.client, cachedNavTreeResponse))), import('./languageFeatures/completions').then(provider => this._register(provider.register(selector, this.description, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.telemetryReporter, this.onCompletionAccepted))), - import('./languageFeatures/copyPaste').then(provider => this._register(provider.register(selector, this.description, this.client))), + import('./languageFeatures/copyPaste').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), import('./languageFeatures/definitions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/documentHighlight').then(provider => this._register(provider.register(selector, this.client))), From 868a93160d9170d36006695e3cbded14d78f09bf Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 12 Dec 2024 13:31:21 -0600 Subject: [PATCH 167/479] Set dropdown container's `aria-disabled` property to announce its disabled state on keyboard focus to screen reader users (#235992) fix #235203 --- .../actions/browser/dropdownWithPrimaryActionViewItem.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts b/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts index f9dec8ce9b5..dbc78139471 100644 --- a/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts +++ b/src/vs/platform/actions/browser/dropdownWithPrimaryActionViewItem.ts @@ -87,6 +87,7 @@ export class DropdownWithPrimaryActionViewItem extends BaseActionViewItem { this._container.classList.add('monaco-dropdown-with-primary'); const primaryContainer = DOM.$('.action-container'); primaryContainer.role = 'button'; + primaryContainer.ariaDisabled = String(!this.action.enabled); this._primaryAction.render(DOM.append(this._container, primaryContainer)); this._dropdownContainer = DOM.$('.dropdown-action-container'); this._dropdown.render(DOM.append(this._container, this._dropdownContainer)); From 1bf794addace3bd8db5c6ea10689e4ba1090e032 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 12 Dec 2024 14:13:41 -0600 Subject: [PATCH 168/479] change condition for adding terminal completion executables (#235994) fix #235082 --- extensions/terminal-suggest/src/terminalSuggestMain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 1fa2c0035da..04af5ff5da9 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -187,7 +187,7 @@ async function getCommandsInPath(): Promise | undefined> { const files = await vscode.workspace.fs.readDirectory(vscode.Uri.file(path)); for (const [file, fileType] of files) { - if (fileType === vscode.FileType.File || fileType === vscode.FileType.SymbolicLink) { + if (fileType !== vscode.FileType.Unknown && fileType !== vscode.FileType.Directory) { executables.add(file); } } From c19df5b00b9408ca693eca5d7a1863668cb15d5e Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 12 Dec 2024 15:24:30 -0600 Subject: [PATCH 169/479] support showIcons setting in terminal completions, simple suggest widget (#235998) support showIcons setting in terminal completions --- .../services/suggest/browser/simpleSuggestWidget.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts index 97417bed34f..6f22b0d0d77 100644 --- a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts +++ b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts @@ -19,6 +19,7 @@ import { localize } from '../../../../nls.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { SuggestWidgetStatus } from '../../../../editor/contrib/suggest/browser/suggestWidgetStatus.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; const $ = dom.$; @@ -86,7 +87,8 @@ export class SimpleSuggestWidget extends Disposable { private readonly _persistedSize: IPersistedWidgetSizeDelegate, private readonly _getFontInfo: () => ISimpleSuggestWidgetFontInfo, options: IWorkbenchSuggestWidgetOptions, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, ) { super(); @@ -141,6 +143,9 @@ export class SimpleSuggestWidget extends Disposable { state = undefined; })); + const applyIconStyle = () => this.element.domNode.classList.toggle('no-icons', !configurationService.getValue('editor.suggest.showIcons')); + applyIconStyle(); + const renderer = new SimpleSuggestWidgetItemRenderer(_getFontInfo); this._register(renderer); this._listElement = dom.append(this.element.domNode, $('.tree')); @@ -196,6 +201,11 @@ export class SimpleSuggestWidget extends Disposable { this._register(this._list.onMouseDown(e => this._onListMouseDownOrTap(e))); this._register(this._list.onTap(e => this._onListMouseDownOrTap(e))); this._register(this._list.onDidChangeSelection(e => this._onListSelection(e))); + this._register(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.suggest.showIcons')) { + applyIconStyle(); + } + })); } private _cursorPosition?: { top: number; left: number; height: number }; From 5980b0f835c2dfa1d85925952cbc91e9ad1ff0b6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 12 Dec 2024 14:46:28 -0800 Subject: [PATCH 170/479] Pick up latest markdown-it-katex --- extensions/markdown-math/package-lock.json | 9 +++++---- extensions/markdown-math/package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-math/package-lock.json b/extensions/markdown-math/package-lock.json index 53a0866e61f..56d5bd40faa 100644 --- a/extensions/markdown-math/package-lock.json +++ b/extensions/markdown-math/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/markdown-it-katex": "^1.1.0" + "@vscode/markdown-it-katex": "^1.1.1" }, "devDependencies": { "@types/markdown-it": "^0.0.0", @@ -32,9 +32,10 @@ "dev": true }, "node_modules/@vscode/markdown-it-katex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.0.tgz", - "integrity": "sha512-9cF2eJpsJOEs2V1cCAoJW/boKz9GQQLvZhNvI030K90z6ZE9lRGc9hDVvKut8zdFO2ObjwylPXXXVYvTdP2O2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.1.tgz", + "integrity": "sha512-3KTlbsRBPJQLE2YmLL7K6nunTlU+W9T5+FjfNdWuIUKgxSS6HWLQHaO3L4MkJi7z7MpIPpY+g4N+cWNBPE/MSA==", + "license": "MIT", "dependencies": { "katex": "^0.16.4" } diff --git a/extensions/markdown-math/package.json b/extensions/markdown-math/package.json index 9669efc2435..5628816d6c3 100644 --- a/extensions/markdown-math/package.json +++ b/extensions/markdown-math/package.json @@ -109,7 +109,7 @@ "build-notebook": "node ./esbuild" }, "dependencies": { - "@vscode/markdown-it-katex": "^1.1.0" + "@vscode/markdown-it-katex": "^1.1.1" }, "devDependencies": { "@types/markdown-it": "^0.0.0", From 4452b11e2ff37f35227a120b412e99abf8e71035 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 12 Dec 2024 14:59:48 -0800 Subject: [PATCH 171/479] Pick up latest TS for building VS Code --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e659e5506fa..fd395ebd432 100644 --- a/package-lock.json +++ b/package-lock.json @@ -153,7 +153,7 @@ "ts-node": "^10.9.1", "tsec": "0.2.7", "tslib": "^2.6.3", - "typescript": "^5.8.0-dev.20241202", + "typescript": "^5.8.0-dev.20241212", "typescript-eslint": "^8.8.0", "util": "^0.12.4", "webpack": "^5.94.0", @@ -17599,9 +17599,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.8.0-dev.20241202", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.0-dev.20241202.tgz", - "integrity": "sha512-4b4+FbKk5Wp51uwCztSyh68rdK1rv4afoC/P/g8HOmWmfHIdCgZNxzLS6TvjBU7e8XVzmW3jE/CJl1n59lcIhg==", + "version": "5.8.0-dev.20241212", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.0-dev.20241212.tgz", + "integrity": "sha512-DL+rd76Ze4iHIFTT6+f8SNdxkTYnR0cy6e0QRljOfyr2s0TrO2L9pAOB1dJnSizTAjxou7lIRpUWwxVOIyiMWg==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 5237906a2c7..eb0abcc3221 100644 --- a/package.json +++ b/package.json @@ -211,7 +211,7 @@ "ts-node": "^10.9.1", "tsec": "0.2.7", "tslib": "^2.6.3", - "typescript": "^5.8.0-dev.20241202", + "typescript": "^5.8.0-dev.20241212", "typescript-eslint": "^8.8.0", "util": "^0.12.4", "webpack": "^5.94.0", From e8a0511293a864439898d53db45303a67b36cf6d Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Thu, 12 Dec 2024 17:33:16 -0800 Subject: [PATCH 172/479] chore: update grooming notebook areas label list (#236005) * chore: update grooming notebook areas label list * chore: update grooming notebook areas label list * chore: update grooming notebook areas label list --- .vscode/notebooks/grooming.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 4b2f8c6750f..4a4955dd767 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -27,7 +27,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:audio-cue -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering" + "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:chat -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering -label:cross-file-editing" }, { "kind": 1, From b425f4802fcbcccb11ad991208fa262c06255be3 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 12 Dec 2024 17:45:44 -0800 Subject: [PATCH 173/479] Check idtoken expiration (#236011) and force expiration in a similar way to the way MSAL does it for access tokens. Fixes https://github.com/microsoft/vscode/issues/229456 --- .../src/node/cachedPublicClientApplication.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts index 2f90e3af3aa..7396da17990 100644 --- a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts +++ b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts @@ -90,8 +90,23 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica async acquireTokenSilent(request: SilentFlowRequest): Promise { this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] starting...`); - const result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(request)); + let result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(request)); this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got result`); + // Check expiration of id token and if it's 5min before expiration, force a refresh. + // this is what MSAL does for access tokens already so we're just adding it for id tokens since we care about those. + const idTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp; + if (idTokenExpirationInSecs) { + const fiveMinutesBefore = new Date( + (idTokenExpirationInSecs - 5 * 60) // subtract 5 minutes + * 1000 // convert to milliseconds + ); + if (fiveMinutesBefore < new Date()) { + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is expired or about to expire. Forcing refresh...`); + result = await this._sequencer.queue(() => this._pca.acquireTokenSilent({ ...request, forceRefresh: true })); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got refreshed result`); + } + } + // this._setupRefresh(result); if (result.account && !result.fromCache && this._verifyIfUsingBroker(result)) { this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] firing event due to change`); From 1e3788f49f234bc37296ce3c5c7b1645605cec08 Mon Sep 17 00:00:00 2001 From: Michael H Date: Fri, 13 Dec 2024 13:29:39 +1100 Subject: [PATCH 174/479] bun.lock --- extensions/npm/src/preferred-pm.ts | 4 ++++ src/vs/workbench/contrib/files/browser/files.contribution.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/npm/src/preferred-pm.ts b/extensions/npm/src/preferred-pm.ts index c85b65b6ea3..452319671ea 100644 --- a/extensions/npm/src/preferred-pm.ts +++ b/extensions/npm/src/preferred-pm.ts @@ -28,6 +28,10 @@ async function isBunPreferred(pkgPath: string): Promise { return { isPreferred: true, hasLockfile: true }; } + if (await pathExists(path.join(pkgPath, 'bun.lock'))) { + return { isPreferred: true, hasLockfile: true }; + } + return { isPreferred: false, hasLockfile: false }; } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 266995ddc6d..689be82dc16 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -619,7 +619,7 @@ configurationRegistry.registerConfiguration({ '*.jsx': '${capture}.js', '*.tsx': '${capture}.ts', 'tsconfig.json': 'tsconfig.*.json', - 'package.json': 'package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb', + 'package.json': 'package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, bun.lock', } } } From fe929f1df2377e3c2c284886d002443d80223de4 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:28:12 +0100 Subject: [PATCH 175/479] Fix panel focus loss when moving the panel (#235971) * fix #111975 --- src/vs/workbench/browser/layout.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 8d731c24025..ca6cb860397 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1787,6 +1787,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const preMoveSideBarSize = !this.isVisible(Parts.SIDEBAR_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.sideBarPartView) ?? this.sideBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.sideBarPartView).width; const preMoveAuxiliaryBarSize = !this.isVisible(Parts.AUXILIARYBAR_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.auxiliaryBarPartView) ?? this.auxiliaryBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.auxiliaryBarPartView).width; + const focusedPart = [Parts.PANEL_PART, Parts.SIDEBAR_PART, Parts.AUXILIARYBAR_PART].find(part => this.hasFocus(part)) as SINGLE_WINDOW_PARTS | undefined; + if (sideBarPosition === Position.LEFT) { this.workbenchGrid.moveViewTo(this.activityBarPartView, [2, 0]); this.workbenchGrid.moveView(this.sideBarPartView, preMoveSideBarSize, sideBarSiblingToEditor ? this.editorPartView : this.activityBarPartView, sideBarSiblingToEditor ? Direction.Left : Direction.Right); @@ -1805,6 +1807,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + // Maintain focus after moving parts + if (focusedPart) { + this.focusPart(focusedPart); + } + // We moved all the side parts based on the editor and ignored the panel // Now, we need to put the panel back in the right position when it is next to the editor if (isPanelVertical) { @@ -2122,6 +2129,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const sideBarVisible = this.isVisible(Parts.SIDEBAR_PART); const auxiliaryBarVisible = this.isVisible(Parts.AUXILIARYBAR_PART); + const hadFocus = this.hasFocus(Parts.PANEL_PART); + if (position === Position.BOTTOM) { this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.height : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT), this.editorPartView, Direction.Down); } else if (position === Position.TOP) { @@ -2132,6 +2141,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.width : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH), this.editorPartView, Direction.Left); } + if (hadFocus) { + this.focusPart(Parts.PANEL_PART); + } + // Reset sidebar to original size before shifting the panel this.workbenchGrid.resizeView(this.sideBarPartView, sideBarSize); if (!sideBarVisible) { From 70af7c835c7c0c9f5982aa3bd5307fdabd816ebb Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:04:05 +0100 Subject: [PATCH 176/479] Merge activity badge items on view containers (#235925) merge activity badge items together and remove priority --- .../workbench/browser/parts/compositeBar.ts | 2 +- .../browser/parts/compositeBarActions.ts | 84 ++++++++++++------- .../browser/parts/globalCompositeBar.ts | 32 +------ .../browser/parts/paneCompositeBar.ts | 3 +- .../contrib/chat/browser/chatSetup.ts | 1 - .../contrib/update/browser/update.ts | 6 +- .../userDataSync/browser/userDataSync.ts | 7 +- .../activity/browser/activityService.ts | 44 +++++----- .../services/activity/common/activity.ts | 1 - .../progress/browser/progressService.ts | 7 +- 10 files changed, 81 insertions(+), 106 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 4da7a57e952..cbc65f19067 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -425,7 +425,7 @@ export class CompositeBar extends Widget implements ICompositeBar { if (item) { // TODO @lramos15 how do we tell the activity to re-render the badge? This triggers an onDidChange but isn't the right way to do it. // I could add another specific function like `activity.updateBadgeEnablement` would then the activity store the sate? - item.activityAction.activity = item.activityAction.activity; + item.activityAction.activities = item.activityAction.activities; } } diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index e22d2dd19e6..726b76aeffd 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -83,10 +83,10 @@ export class CompositeBarAction extends Action { private readonly _onDidChangeCompositeBarActionItem = this._register(new Emitter()); readonly onDidChangeCompositeBarActionItem = this._onDidChangeCompositeBarActionItem.event; - private readonly _onDidChangeActivity = this._register(new Emitter()); + private readonly _onDidChangeActivity = this._register(new Emitter()); readonly onDidChangeActivity = this._onDidChangeActivity.event; - private _activity: IActivity | undefined; + private _activities: IActivity[] = []; constructor(private item: ICompositeBarActionItem) { super(item.id, item.name, item.classNames?.join(' '), true); @@ -102,13 +102,13 @@ export class CompositeBarAction extends Action { this._onDidChangeCompositeBarActionItem.fire(this); } - get activity(): IActivity | undefined { - return this._activity; + get activities(): IActivity[] { + return this._activities; } - set activity(activity: IActivity | undefined) { - this._activity = activity; - this._onDidChangeActivity.fire(activity); + set activities(activities: IActivity[]) { + this._activities = activities; + this._onDidChangeActivity.fire(activities); } activate(): void { @@ -214,7 +214,7 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { // Badge if (this.badgeContent) { - const badgeStyles = this.getActivity()?.badge.getColors(theme); + const badgeStyles = this.getActivities()[0]?.badge.getColors(theme); const badgeFg = badgeStyles?.badgeForeground ?? colors.badgeForeground ?? theme.getColor(badgeForeground); const badgeBg = badgeStyles?.badgeBackground ?? colors.badgeBackground ?? theme.getColor(badgeBackground); const contrastBorderColor = badgeStyles?.badgeBorder ?? theme.getColor(contrastBorder); @@ -300,11 +300,11 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { this.updateStyles(); } - private getActivity(): IActivity | undefined { + private getActivities(): IActivity[] { if (this._action instanceof CompositeBarAction) { - return this._action.activity; + return this._action.activities; } - return undefined; + return []; } protected updateActivity(): void { @@ -312,7 +312,7 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { return; } - const activity = this.getActivity(); + const { badges, type } = this.getVisibleBadges(this.getActivities()); this.badgeDisposable.value = new DisposableStore(); @@ -321,9 +321,8 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { const shouldRenderBadges = this.badgesEnabled(this.compositeBarActionItem.id); - if (activity && shouldRenderBadges) { + if (badges.length > 0 && shouldRenderBadges) { - const { badge } = activity; const classes: string[] = []; if (this.options.compact) { @@ -331,36 +330,33 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { } // Progress - if (badge instanceof ProgressBadge) { + if (type === 'progress') { show(this.badge); classes.push('progress-badge'); } // Number - else if (badge instanceof NumberBadge) { - if (badge.number) { - let number = badge.number.toString(); - if (badge.number > 999) { - const noOfThousands = badge.number / 1000; + else if (type === 'number') { + const total = badges.reduce((r, b) => r + (b instanceof NumberBadge ? b.number : 0), 0); + if (total > 0) { + let badgeNumber = total.toString(); + if (total > 999) { + const noOfThousands = total / 1000; const floor = Math.floor(noOfThousands); - if (noOfThousands > floor) { - number = `${floor}K+`; - } else { - number = `${noOfThousands}K`; - } + badgeNumber = noOfThousands > floor ? `${floor}K+` : `${noOfThousands}K`; } - if (this.options.compact && number.length >= 3) { + if (this.options.compact && badgeNumber.length >= 3) { classes.push('compact-content'); } - this.badgeContent.textContent = number; + this.badgeContent.textContent = badgeNumber; show(this.badge); } } // Icon - else if (badge instanceof IconBadge) { + else if (type === 'icon') { classes.push('icon-badge'); - const badgeContentClassess = ['icon-overlay', ...ThemeIcon.asClassNameArray(badge.icon)]; + const badgeContentClassess = ['icon-overlay', ...ThemeIcon.asClassNameArray((badges[0] as IconBadge).icon)]; this.badgeContent.classList.add(...badgeContentClassess); this.badgeDisposable.value.add(toDisposable(() => this.badgeContent?.classList.remove(...badgeContentClassess))); show(this.badge); @@ -377,6 +373,25 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { this.updateStyles(); } + private getVisibleBadges(activities: IActivity[]): { badges: IBadge[]; type: 'progress' | 'icon' | 'number' | undefined } { + const progressBadges = activities.filter(activity => activity.badge instanceof ProgressBadge).map(activity => activity.badge); + if (progressBadges.length > 0) { + return { badges: progressBadges, type: 'progress' }; + } + + const iconBadges = activities.filter(activity => activity.badge instanceof IconBadge).map(activity => activity.badge); + if (iconBadges.length > 0) { + return { badges: iconBadges, type: 'icon' }; + } + + const numberBadges = activities.filter(activity => activity.badge instanceof NumberBadge).map(activity => activity.badge); + if (numberBadges.length > 0) { + return { badges: numberBadges, type: 'number' }; + } + + return { badges: [], type: undefined }; + } + protected override updateLabel(): void { this.label.className = 'action-label'; @@ -403,9 +418,14 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { protected computeTitle(): string { this.keybindingLabel = this.computeKeybindingLabel(); let title = this.keybindingLabel ? localize('titleKeybinding', "{0} ({1})", this.compositeBarActionItem.name, this.keybindingLabel) : this.compositeBarActionItem.name; - const badge = (this.action as CompositeBarAction).activity?.badge; - if (badge?.getDescription()) { - title = localize('badgeTitle', "{0} - {1}", title, badge.getDescription()); + + const badges = this.getVisibleBadges((this.action as CompositeBarAction).activities).badges; + for (const badge of badges) { + const description = badge.getDescription(); + if (!description) { + continue; + } + title = `${title} - ${badge.getDescription()}`; } return title; diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index d93cb9b1ef1..c026e158172 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -6,7 +6,7 @@ import { localize } from '../../../nls.js'; import { ActionBar, ActionsOrientation } from '../../../base/browser/ui/actionbar/actionbar.js'; import { ACCOUNTS_ACTIVITY_ID, GLOBAL_ACTIVITY_ID } from '../../common/activity.js'; -import { IActivity, IActivityService, NumberBadge } from '../../services/activity/common/activity.js'; +import { IActivityService } from '../../services/activity/common/activity.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { DisposableStore, Disposable } from '../../../base/common/lifecycle.js'; import { IColorTheme, IThemeService } from '../../../platform/theme/common/themeService.js'; @@ -183,33 +183,7 @@ abstract class AbstractGlobalActivityActionViewItem extends CompositeBarActionVi } private updateItemActivity(): void { - const activities = this.activityService.getActivity(this.compositeBarActionItem.id); - let activity = activities[0]; - if (activity) { - const { badge, priority } = activity; - if (badge instanceof NumberBadge && activities.length > 1) { - const cumulativeNumberBadge = this.getCumulativeNumberBadge(activities, priority ?? 0); - activity = { badge: cumulativeNumberBadge }; - } - } - (this.action as CompositeBarAction).activity = activity; - } - - private getCumulativeNumberBadge(activityCache: IActivity[], priority: number): NumberBadge { - const numberActivities = activityCache.filter(activity => activity.badge instanceof NumberBadge && (activity.priority ?? 0) === priority); - const number = numberActivities.reduce((result, activity) => { return result + (activity.badge).number; }, 0); - const descriptorFn = (): string => { - return numberActivities.reduce((result, activity, index) => { - result = result + (activity.badge).getDescription(); - if (index < numberActivities.length - 1) { - result = `${result}\n`; - } - - return result; - }, ''); - }; - - return new NumberBadge(number, descriptorFn); + (this.action as CompositeBarAction).activities = this.activityService.getActivity(this.compositeBarActionItem.id); } override render(container: HTMLElement): void { @@ -595,7 +569,7 @@ export class GlobalActivityActionViewItem extends AbstractGlobalActivityActionVi return; } - if ((this.action as CompositeBarAction).activity) { + if ((this.action as CompositeBarAction).activities.length > 0) { return; } diff --git a/src/vs/workbench/browser/parts/paneCompositeBar.ts b/src/vs/workbench/browser/parts/paneCompositeBar.ts index db3d98034a0..05b43a296e1 100644 --- a/src/vs/workbench/browser/parts/paneCompositeBar.ts +++ b/src/vs/workbench/browser/parts/paneCompositeBar.ts @@ -795,8 +795,7 @@ class ViewContainerActivityAction extends CompositeBarAction { } private updateActivity(): void { - const activities = this.activityService.getViewContainerActivities(this.compositeBarActionItem.id); - this.activity = activities[0]; + this.activities = this.activityService.getViewContainerActivities(this.compositeBarActionItem.id); } override async run(event: { preserveFocus: boolean }): Promise { diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 3af2ce5f98c..0f9b0496a49 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -682,7 +682,6 @@ class ChatSetupController extends Disposable { const title = localize('setupChatProgress', "Getting Copilot ready..."); const badge = this.activityService.showViewContainerActivity(isCopilotEditsViewActive(this.viewsService) ? CHAT_EDITING_SIDEBAR_PANEL_ID : CHAT_SIDEBAR_PANEL_ID, { badge: new ProgressBadge(() => title), - priority: 100 }); try { diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index d824df32c93..fd3452f5f02 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -254,25 +254,21 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu } let badge: IBadge | undefined = undefined; - let priority: number | undefined = undefined; if (state.type === StateType.AvailableForDownload || state.type === StateType.Downloaded || state.type === StateType.Ready) { badge = new NumberBadge(1, () => nls.localize('updateIsReady', "New {0} update available.", this.productService.nameShort)); } else if (state.type === StateType.CheckingForUpdates) { badge = new ProgressBadge(() => nls.localize('checkingForUpdates', "Checking for {0} updates...", this.productService.nameShort)); - priority = 1; } else if (state.type === StateType.Downloading) { badge = new ProgressBadge(() => nls.localize('downloading', "Downloading {0} update...", this.productService.nameShort)); - priority = 1; } else if (state.type === StateType.Updating) { badge = new ProgressBadge(() => nls.localize('updating', "Updating {0}...", this.productService.nameShort)); - priority = 1; } this.badgeDisposable.clear(); if (badge) { - this.badgeDisposable.value = this.activityService.showGlobalActivity({ badge, priority }); + this.badgeDisposable.value = this.activityService.showGlobalActivity({ badge }); } this.state = state; diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index c7f9cb61c25..6a2c8ceb17a 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -426,17 +426,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.globalActivityBadgeDisposable.clear(); let badge: IBadge | undefined = undefined; - let priority: number | undefined = undefined; - if (this.userDataSyncService.conflicts.length && this.userDataSyncEnablementService.isEnabled()) { badge = new NumberBadge(this.getConflictsCount(), () => localize('has conflicts', "{0}: Conflicts Detected", SYNC_TITLE.value)); } else if (this.turningOnSync) { badge = new ProgressBadge(() => localize('turning on syncing', "Turning on Settings Sync...")); - priority = 1; } if (badge) { - this.globalActivityBadgeDisposable.value = this.activityService.showGlobalActivity({ badge, priority }); + this.globalActivityBadgeDisposable.value = this.activityService.showGlobalActivity({ badge }); } } @@ -450,7 +447,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } if (badge) { - this.accountBadgeDisposable.value = this.activityService.showAccountsActivity({ badge, priority: undefined }); + this.accountBadgeDisposable.value = this.activityService.showAccountsActivity({ badge }); } } diff --git a/src/vs/workbench/services/activity/browser/activityService.ts b/src/vs/workbench/services/activity/browser/activityService.ts index 2d6615626fe..03eedc1c3cc 100644 --- a/src/vs/workbench/services/activity/browser/activityService.ts +++ b/src/vs/workbench/services/activity/browser/activityService.ts @@ -10,7 +10,6 @@ import { IViewDescriptorService, ViewContainer } from '../../../common/views.js' import { GLOBAL_ACTIVITY_ID, ACCOUNTS_ACTIVITY_ID } from '../../../common/activity.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { isUndefined } from '../../../../base/common/types.js'; class ViewContainerActivityByView extends Disposable { @@ -77,31 +76,28 @@ export class ActivityService extends Disposable implements IActivityService { showViewContainerActivity(viewContainerId: string, activity: IActivity): IDisposable { const viewContainer = this.viewDescriptorService.getViewContainerById(viewContainerId); - if (viewContainer) { - let activities = this.viewContainerActivities.get(viewContainerId); - if (!activities) { - activities = []; - this.viewContainerActivities.set(viewContainerId, activities); - } - for (let i = 0; i <= activities.length; i++) { - if (i === activities.length || isUndefined(activity.priority)) { - activities.push(activity); - break; - } else if (isUndefined(activities[i].priority) || activities[i].priority! <= activity.priority) { - activities.splice(i, 0, activity); - break; - } + if (!viewContainer) { + return Disposable.None; + } + + let activities = this.viewContainerActivities.get(viewContainerId); + if (!activities) { + activities = []; + this.viewContainerActivities.set(viewContainerId, activities); + } + + // add activity + activities.push(activity); + + this._onDidChangeActivity.fire(viewContainer); + + return toDisposable(() => { + activities.splice(activities.indexOf(activity), 1); + if (activities.length === 0) { + this.viewContainerActivities.delete(viewContainerId); } this._onDidChangeActivity.fire(viewContainer); - return toDisposable(() => { - activities.splice(activities.indexOf(activity), 1); - if (activities.length === 0) { - this.viewContainerActivities.delete(viewContainerId); - } - this._onDidChangeActivity.fire(viewContainer); - }); - } - return Disposable.None; + }); } getViewContainerActivities(viewContainerId: string): IActivity[] { diff --git a/src/vs/workbench/services/activity/common/activity.ts b/src/vs/workbench/services/activity/common/activity.ts index 38e03498cb4..4b114f37b40 100644 --- a/src/vs/workbench/services/activity/common/activity.ts +++ b/src/vs/workbench/services/activity/common/activity.ts @@ -15,7 +15,6 @@ import { ViewContainer } from '../../../common/views.js'; export interface IActivity { readonly badge: IBadge; - readonly priority?: number; } export const IActivityService = createDecorator('activityService'); diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index c0f5fb3929c..872300d3937 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -454,11 +454,6 @@ export class ProgressService extends Disposable implements IProgressService { const progressIndicator = this.viewsService.getViewProgressIndicator(viewId); const promise = progressIndicator ? this.withCompositeProgress(progressIndicator, task, options) : task({ report: () => { } }); - const location = this.viewDescriptorService.getViewLocationById(viewId); - if (location !== ViewContainerLocation.Sidebar) { - return promise; - } - const viewletId = this.viewDescriptorService.getViewContainerByViewId(viewId)?.id; if (viewletId === undefined) { return promise; @@ -474,7 +469,7 @@ export class ProgressService extends Disposable implements IProgressService { let activityProgress: IDisposable; let delayHandle: any = setTimeout(() => { delayHandle = undefined; - const handle = this.activityService.showViewContainerActivity(viewletId, { badge: new ProgressBadge(() => ''), priority: 100 }); + const handle = this.activityService.showViewContainerActivity(viewletId, { badge: new ProgressBadge(() => '') }); const startTimeVisible = Date.now(); const minTimeVisible = 300; activityProgress = { From 2b5593f324fe46db692de77cfd173bb8699c9f4b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 13 Dec 2024 11:09:10 +0100 Subject: [PATCH 177/479] Proper fix for #235977 (#235983) --- .../base/common/observableInternal/derived.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index 5d7fc0a7bfb..80cc9a72124 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -365,6 +365,8 @@ export class Derived extends BaseObservable im } } + private _removedObserverToCallEndUpdateOn: Set | null = null; + public endUpdate(_observable: IObservable): void { this.updateCount--; if (this.updateCount === 0) { @@ -373,6 +375,13 @@ export class Derived extends BaseObservable im for (const r of observers) { r.endUpdate(this); } + if (this._removedObserverToCallEndUpdateOn) { + const observers = [...this._removedObserverToCallEndUpdateOn]; + this._removedObserverToCallEndUpdateOn = null; + for (const r of observers) { + r.endUpdate(this); + } + } } assertFn(() => this.updateCount >= 0); } @@ -433,18 +442,22 @@ export class Derived extends BaseObservable im super.addObserver(observer); if (shouldCallBeginUpdate) { - observer.beginUpdate(this); + if (this._removedObserverToCallEndUpdateOn && this._removedObserverToCallEndUpdateOn.has(observer)) { + this._removedObserverToCallEndUpdateOn.delete(observer); + } else { + observer.beginUpdate(this); + } } } public override removeObserver(observer: IObserver): void { - const shouldCallEndUpdate = this.observers.has(observer) && this.updateCount > 0; - super.removeObserver(observer); - - if (shouldCallEndUpdate) { - // Calling end update after removing the observer makes sure endUpdate cannot be called twice here. - observer.endUpdate(this); + if (this.observers.has(observer) && this.updateCount > 0) { + if (!this._removedObserverToCallEndUpdateOn) { + this._removedObserverToCallEndUpdateOn = new Set(); + } + this._removedObserverToCallEndUpdateOn.add(observer); } + super.removeObserver(observer); } } From 1a5b1f329ad136ede86940844a48fc6f6600286a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 Dec 2024 11:15:08 +0100 Subject: [PATCH 178/479] register command to open the active log with the macos native log viewer (#235963) * register command to open the active log with the macos native log viewer helps with https://github.com/microsoft/vscode-copilot/issues/11205 * make layer check happy --- .../electron-sandbox/output.contribution.ts | 46 +++++++++++++++++++ src/vs/workbench/workbench.desktop.main.ts | 6 +-- 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/vs/workbench/contrib/output/electron-sandbox/output.contribution.ts diff --git a/src/vs/workbench/contrib/output/electron-sandbox/output.contribution.ts b/src/vs/workbench/contrib/output/electron-sandbox/output.contribution.ts new file mode 100644 index 00000000000..7305828c10f --- /dev/null +++ b/src/vs/workbench/contrib/output/electron-sandbox/output.contribution.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Schemas } from '../../../../base/common/network.js'; +import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; +import { localize2 } from '../../../../nls.js'; +import { registerAction2, Action2, MenuId } from '../../../../platform/actions/common/actions.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { IsMacNativeContext } from '../../../../platform/contextkey/common/contextkeys.js'; +import { INativeHostService } from '../../../../platform/native/common/native.js'; +import { OUTPUT_VIEW_ID, CONTEXT_ACTIVE_FILE_OUTPUT, IOutputService } from '../../../services/output/common/output.js'; + + +registerAction2(class OpenInConsoleAction extends Action2 { + constructor() { + super({ + id: `workbench.action.openActiveLogOutputFileNative`, + title: localize2('openActiveOutputFileNative', "Open Output in Console"), + menu: [{ + id: MenuId.ViewTitle, + when: ContextKeyExpr.and(ContextKeyExpr.equals('view', OUTPUT_VIEW_ID), IsMacNativeContext), + group: 'navigation', + order: 6, + isHiddenByDefault: true + }], + icon: Codicon.goToFile, + precondition: ContextKeyExpr.and(CONTEXT_ACTIVE_FILE_OUTPUT, IsMacNativeContext) + }); + } + + async run(accessor: ServicesAccessor): Promise { + const outputService = accessor.get(IOutputService); + const hostService = accessor.get(INativeHostService); + const channel = outputService.getActiveChannel(); + if (!channel) { + return; + } + const descriptor = outputService.getChannelDescriptors().find(c => c.id === channel.id); + if (descriptor?.file && descriptor.file.scheme === Schemas.file) { + hostService.openExternal(descriptor.file.toString(true), 'open'); + } + } +}); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 57bf9f0167c..755d3c95beb 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -127,20 +127,21 @@ import './contrib/remote/electron-sandbox/remote.contribution.js'; // Configuration Exporter import './contrib/configExporter/electron-sandbox/configurationExportHelper.contribution.js'; +// Output View +import './contrib/output/electron-sandbox/output.contribution.js'; + // Terminal import './contrib/terminal/electron-sandbox/terminal.contribution.js'; // Themes import './contrib/themes/browser/themes.test.contribution.js'; import './services/themes/electron-sandbox/themes.contribution.js'; - // User Data Sync import './contrib/userDataSync/electron-sandbox/userDataSync.contribution.js'; // Tags import './contrib/tags/electron-sandbox/workspaceTagsService.js'; import './contrib/tags/electron-sandbox/tags.contribution.js'; - // Performance import './contrib/performance/electron-sandbox/performance.contribution.js'; @@ -171,7 +172,6 @@ import './contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.js'; // Chat import './contrib/chat/electron-sandbox/chat.contribution.js'; import './contrib/inlineChat/electron-sandbox/inlineChat.contribution.js'; - // Encryption import './contrib/encryption/electron-sandbox/encryption.contribution.js'; From d608eb8aca4db80935f4f2a87338d5fa2239bbc5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 Dec 2024 11:41:43 +0100 Subject: [PATCH 179/479] fix https://github.com/microsoft/vscode-copilot/issues/10983 (#236022) --- .../browser/contrib/chatInputCompletions.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 6bde0e0e6d8..06a7bd3c912 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -86,7 +86,7 @@ class SlashCommandCompletions extends Disposable { return { label: withSlash, insertText: c.executeImmediately ? '' : `${withSlash} `, - detail: c.detail, + documentation: c.detail, range, sortText: c.sortText ?? 'a'.repeat(i + 1), kind: CompletionItemKind.Text, // The icons are disabled here anyway, @@ -126,7 +126,7 @@ class SlashCommandCompletions extends Disposable { return { label: withSlash, insertText: c.executeImmediately ? '' : `${withSlash} `, - detail: c.detail, + documentation: c.detail, range, filterText: `${chatAgentLeader}${c.command}`, sortText: c.sortText ?? 'z'.repeat(i + 1), @@ -193,7 +193,7 @@ class AgentCompletions extends Disposable { return { label: withSlash, insertText: `${withSlash} `, - detail: c.description, + documentation: c.description, range, kind: CompletionItemKind.Text, // The icons are disabled here anyway }; @@ -246,7 +246,7 @@ class AgentCompletions extends Disposable { label: isDupe ? { label: agentLabel, description: agent.description, detail: ` (${agent.publisherDisplayName})` } : agentLabel, - detail, + documentation: detail, filterText: `${chatAgentLeader}${agent.name}`, insertText: `${agentLabel} `, range, @@ -265,7 +265,7 @@ class AgentCompletions extends Disposable { label: isDupe ? { label, description: c.description, detail: isDupe ? ` (${agent.publisherDisplayName})` : undefined } : label, - detail: c.description, + documentation: c.description, filterText: getFilterText(agent, c.name), commitCharacters: [' '], insertText: label + ' ', @@ -280,7 +280,7 @@ class AgentCompletions extends Disposable { const label = `${chatSubcommandLeader}${c.name}`; item.label = label; item.insertText = `${label} `; - item.detail = c.description; + item.documentation = c.description; } return item; @@ -322,7 +322,7 @@ class AgentCompletions extends Disposable { label: { label: withSlash, description: agentLabel, detail: isDupe ? ` (${agent.publisherDisplayName})` : undefined }, commitCharacters: [' '], insertText: `${agentLabel} ${withSlash} `, - detail: `(${agentLabel}) ${c.description ?? ''}`, + documentation: `(${agentLabel}) ${c.description ?? ''}`, range, kind: CompletionItemKind.Text, // The icons are disabled here anyway sortText, @@ -334,7 +334,7 @@ class AgentCompletions extends Disposable { const label = `${chatSubcommandLeader}${c.name}`; item.label = label; item.insertText = `${label} `; - item.detail = c.description; + item.documentation = c.description; } return item; @@ -464,7 +464,7 @@ class BuiltinDynamicCompletions extends Disposable { result.suggestions.push({ label: `${chatVariableLeader}file`, insertText: `${chatVariableLeader}file:`, - detail: localize('pickFileLabel', "Pick a file"), + documentation: localize('pickFileLabel', "Pick a file"), range, kind: CompletionItemKind.Text, command: { id: SelectAndInsertFileAction.ID, title: SelectAndInsertFileAction.ID, arguments: [{ widget, range: afterRange }] }, @@ -499,7 +499,7 @@ class BuiltinDynamicCompletions extends Disposable { result.suggestions.push({ label: `${chatVariableLeader}sym`, insertText: `${chatVariableLeader}sym:`, - detail: localize('pickSymbolLabel', "Pick a symbol"), + documentation: localize('pickSymbolLabel', "Pick a symbol"), range, kind: CompletionItemKind.Text, command: { id: SelectAndInsertSymAction.ID, title: SelectAndInsertSymAction.ID, arguments: [{ widget, range: afterRangeSym }] }, @@ -798,7 +798,7 @@ class VariableCompletions extends Disposable { label: withLeader, range, insertText: withLeader + ' ', - detail: v.description, + documentation: v.description, kind: CompletionItemKind.Text, // The icons are disabled here anyway sortText: 'z' }; @@ -816,7 +816,7 @@ class VariableCompletions extends Disposable { label: withLeader, range, insertText: withLeader + ' ', - detail: t.userDescription, + documentation: t.userDescription, kind: CompletionItemKind.Text, sortText: 'z' }; From a793c5919550f11514c460dc58eaf1ea07629f00 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 13 Dec 2024 11:44:06 +0100 Subject: [PATCH 180/479] chat - handle error situations and present to user (#235918) --- .../contrib/chat/browser/chatSetup.ts | 70 +++++++++++++++++-- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 0f9b0496a49..a9b5eafe8eb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -59,6 +59,7 @@ import { mainWindow } from '../../../../base/browser/window.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { URI } from '../../../../base/common/uri.js'; import { IHostService } from '../../../services/host/browser/host.js'; +import Severity from '../../../../base/common/severity.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -346,6 +347,8 @@ class ChatSetupRequests extends Disposable { @ILogService private readonly logService: ILogService, @IRequestService private readonly requestService: IRequestService, @IChatQuotasService private readonly chatQuotasService: IChatQuotasService, + @IDialogService private readonly dialogService: IDialogService, + @IOpenerService private readonly openerService: IOpenerService ) { super(); @@ -467,7 +470,12 @@ class ChatSetupRequests extends Disposable { return { entitlement: ChatEntitlement.Unresolved }; } - const responseText = await asText(response); + let responseText: string | null = null; + try { + responseText = await asText(response); + } catch (error) { + // ignore - handled below + } if (token.isCancellationRequested) { return undefined; } @@ -570,18 +578,38 @@ class ChatSetupRequests extends Disposable { const response = await this.request(defaultChat.entitlementSignupLimitedUrl, 'POST', body, session, CancellationToken.None); if (!response) { - this.logService.error('[chat setup] sign-up: no response'); + this.onUnknownSignUpError('[chat setup] sign-up: no response'); return false; } if (response.res.statusCode && response.res.statusCode !== 200) { - this.logService.error(`[chat setup] sign-up: unexpected status code ${response.res.statusCode}`); + if (response.res.statusCode === 422) { + try { + const responseText = await asText(response); + if (responseText) { + const responseError: { message: string } = JSON.parse(responseText); + if (typeof responseError.message === 'string' && responseError.message) { + this.onUnprocessableSignUpError(`[chat setup] sign-up: unprocessable entity (${responseError.message})`, responseError.message); + return false; + } + } + } catch (error) { + // ignore - handled below + } + } + this.onUnknownSignUpError(`[chat setup] sign-up: unexpected status code ${response.res.statusCode}`); return false; } - const responseText = await asText(response); + let responseText: string | null = null; + try { + responseText = await asText(response); + } catch (error) { + // ignore - handled below + } + if (!responseText) { - this.logService.error('[chat setup] sign-up: response has no content'); + this.onUnknownSignUpError('[chat setup] sign-up: response has no content'); return false; } @@ -590,7 +618,7 @@ class ChatSetupRequests extends Disposable { parsedResult = JSON.parse(responseText); this.logService.trace(`[chat setup] sign-up: response is ${responseText}`); } catch (err) { - this.logService.error(`[chat setup] sign-up: error parsing response (${err})`); + this.onUnknownSignUpError(`[chat setup] sign-up: error parsing response (${err})`); } const subscribed = Boolean(parsedResult?.subscribed); @@ -607,6 +635,30 @@ class ChatSetupRequests extends Disposable { return subscribed; } + private onUnknownSignUpError(logMessage: string): void { + this.dialogService.error(localize('unknownSignUpError', "An error occurred while signing up for Copilot Free."), localize('unknownSignUpErrorDetail', "Please try again.")); + this.logService.error(logMessage); + } + + private onUnprocessableSignUpError(logMessage: string, logDetails: string): void { + this.dialogService.prompt({ + type: Severity.Error, + message: localize('unprocessableSignUpError', "An error occurred while signing up for Copilot Free."), + detail: logDetails, + buttons: [ + { + label: localize('ok', "OK"), + run: () => { /* noop */ } + }, + { + label: localize('learnMore', "Learn More"), + run: () => this.openerService.open(URI.parse(defaultChat.upgradePlanUrl)) + } + ] + }); + this.logService.error(logMessage); + } + override dispose(): void { this.pendingResolveCts.dispose(true); @@ -625,7 +677,7 @@ type InstallChatClassification = { signedIn: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user did sign in prior to installing the extension.' }; }; type InstallChatEvent = { - installResult: 'installed' | 'cancelled' | 'failedInstall' | 'failedNotSignedIn'; + installResult: 'installed' | 'cancelled' | 'failedInstall' | 'failedNotSignedIn' | 'failedSignUp'; signedIn: boolean; }; @@ -770,6 +822,10 @@ class ChatSetupController extends Disposable { if (entitlement !== ChatEntitlement.Limited && entitlement !== ChatEntitlement.Pro && entitlement !== ChatEntitlement.Unavailable) { didSignUp = await this.requests.signUpLimited(session); + + if (!didSignUp) { + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedSignUp', signedIn }); + } } await this.extensionsWorkbenchService.install(defaultChat.extensionId, { From 708f5ab818017bee22e6be822283f1c7b8a7813f Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:54:06 +0100 Subject: [PATCH 181/479] Dim disabled SVG icons in action bar (#236024) fix #209397 --- src/vs/base/browser/ui/actionbar/actionbar.css | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css index cf699047f02..6471258102e 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.css +++ b/src/vs/base/browser/ui/actionbar/actionbar.css @@ -52,12 +52,19 @@ border-radius: 5px; } -.monaco-action-bar .action-item.disabled .action-label, -.monaco-action-bar .action-item.disabled .action-label::before, -.monaco-action-bar .action-item.disabled .action-label:hover { +.monaco-action-bar .action-item.disabled .action-label:not(.icon) , +.monaco-action-bar .action-item.disabled .action-label:not(.icon)::before, +.monaco-action-bar .action-item.disabled .action-label:not(.icon):hover { color: var(--vscode-disabledForeground); } +/* Unable to change color of SVGs, hence opacity is used */ +.monaco-action-bar .action-item.disabled .action-label.icon , +.monaco-action-bar .action-item.disabled .action-label.icon::before, +.monaco-action-bar .action-item.disabled .action-label.icon:hover { + opacity: 0.6; +} + /* Vertical actions */ .monaco-action-bar.vertical { From bb212e4b0cc954d642270a14c9c436ec760ddb42 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 13 Dec 2024 11:54:54 +0100 Subject: [PATCH 182/479] Better react to conditions which would cause a line to change its position vertically --- src/vs/editor/browser/observableCodeEditor.ts | 34 +++++++++++++++++-- src/vs/editor/browser/point.ts | 28 +++++++++++++++ .../view/inlineEdits/sideBySideDiff.ts | 29 ++++++++++++---- .../browser/view/inlineEdits/utils.ts | 30 ++++------------ 4 files changed, 87 insertions(+), 34 deletions(-) create mode 100644 src/vs/editor/browser/point.ts diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index 324bdd0528b..404f10dbe46 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -12,7 +12,8 @@ import { Selection } from '../common/core/selection.js'; import { ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; import { IModelDeltaDecoration, ITextModel } from '../common/model.js'; import { IModelContentChangedEvent } from '../common/textModelEvents.js'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from './editorBrowser.js'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IOverlayWidget, IOverlayWidgetPosition } from './editorBrowser.js'; +import { Point } from './point.js'; /** * Returns a facade for the code editor that provides observables for various states/events. @@ -241,10 +242,10 @@ export class ObservableCodeEditor extends Disposable { return d; } - private _overlayWidgetCounter = 0; + private _widgetCounter = 0; public createOverlayWidget(widget: IObservableOverlayWidget): IDisposable { - const overlayWidgetId = 'observableOverlayWidget' + (this._overlayWidgetCounter++); + const overlayWidgetId = 'observableOverlayWidget' + (this._widgetCounter++); const w: IOverlayWidget = { getDomNode: () => widget.domNode, getPosition: () => widget.position.get(), @@ -263,6 +264,33 @@ export class ObservableCodeEditor extends Disposable { this.editor.removeOverlayWidget(w); }); } + + public observePosition(position: IObservable, store: DisposableStore): IObservable { + const result = observableValueOpts({ owner: this, equalsFn: equalsIfDefined(Point.equals) }, new Point(0, 0)); + const contentWidgetId = `observablePositionWidget` + (this._widgetCounter++); + const domNode = document.createElement('div'); + const w: IContentWidget = { + getDomNode: () => domNode, + getPosition: () => { + const pos = position.get(); + return pos ? { preference: [ContentWidgetPositionPreference.EXACT], position: position.get() } : null; + }, + getId: () => contentWidgetId, + allowEditorOverflow: false, + afterRender(position, coordinate) { + result.set(coordinate ? new Point(coordinate.left, coordinate.top) : null, undefined); + }, + }; + this.editor.addContentWidget(w); + store.add(autorun(reader => { + position.read(reader); + this.editor.layoutContentWidget(w); + })); + store.add(toDisposable(() => { + this.editor.removeContentWidget(w); + })); + return result; + } } interface IObservableOverlayWidget { diff --git a/src/vs/editor/browser/point.ts b/src/vs/editor/browser/point.ts new file mode 100644 index 00000000000..d056d1218f8 --- /dev/null +++ b/src/vs/editor/browser/point.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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 add(other: Point): Point { + return new Point(this.x + other.x, this.y + other.y); + } + + public deltaX(delta: number): Point { + return new Point(this.x + delta, this.y); + } + + public deltaY(delta: number): Point { + return new Point(this.x, this.y + delta); + } +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 90f026719a3..25b1d750fc4 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -7,25 +7,27 @@ import { IAction } from '../../../../../../base/common/actions.js'; import { Color } from '../../../../../../base/common/color.js'; import { structuralEquals } from '../../../../../../base/common/equals.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IObservable, constObservable, derived, autorun, derivedOpts, observableValue } from '../../../../../../base/common/observable.js'; +import { IObservable, autorun, constObservable, derived, derivedOpts, observableValue } from '../../../../../../base/common/observable.js'; import { MenuId, MenuItemAction } from '../../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { diffRemoved, diffInserted } from '../../../../../../platform/theme/common/colorRegistry.js'; -import { registerColor, darken, lighten } from '../../../../../../platform/theme/common/colorUtils.js'; +import { diffInserted, diffRemoved } from '../../../../../../platform/theme/common/colorRegistry.js'; +import { darken, lighten, registerColor } from '../../../../../../platform/theme/common/colorUtils.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { Point } from '../../../../../browser/point.js'; import { EmbeddedCodeEditorWidget } from '../../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { editorLineHighlightBorder } from '../../../../../common/core/editorColorRegistry.js'; import { LineRange } from '../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../common/core/offsetRange.js'; +import { Position } from '../../../../../common/core/position.js'; import { Range } from '../../../../../common/core/range.js'; import { Command } from '../../../../../common/languages.js'; import { ITextModel } from '../../../../../common/model.js'; import { CustomizedMenuWorkbenchToolBar } from '../../hintsWidget/inlineCompletionsHintsWidget.js'; +import { PathBuilder, StatusBarViewItem, getOffsetForPos, mapOutFalsy, maxContentWidthInRange, n } from './utils.js'; import { InlineEditWithChanges } from './viewAndDiffProducer.js'; -import { StatusBarViewItem, maxContentWidthInRange, getOffsetForPos, Point, n, PathBuilder, mapOutFalsy } from './utils.js'; export const originalBackgroundColor = registerColor( @@ -290,10 +292,23 @@ export class InlineEditsSideBySideDiff extends Disposable { return edit.modifiedLineRange.contains(cursorPos.lineNumber) ? cursorPos : undefined; }); + private readonly _originalStartPosition = derived(this, (reader) => { + const inlineEdit = this._edit.read(reader); + return inlineEdit ? new Position(inlineEdit.originalLineRange.startLineNumber, 1) : null; + }); + + private readonly _originalEndPosition = derived(this, (reader) => { + const inlineEdit = this._edit.read(reader); + return inlineEdit ? new Position(inlineEdit.originalLineRange.endLineNumberExclusive, 1) : null; + }); + + private readonly _originalVerticalStartPosition = this._editorObs.observePosition(this._originalStartPosition, this._store).map(p => p?.y); + private readonly _originalVerticalEndPosition = this._editorObs.observePosition(this._originalEndPosition, this._store).map(p => p?.y); + /** * ![test](./layout.dio.svg) */ - private readonly _previewEditorLayoutInfo = derived(this, (reader) => { + private readonly _previewEditorLayoutInfo = derived(this, (reader) => {// const inlineEdit = this._edit.read(reader); if (!inlineEdit) { return null; @@ -328,8 +343,8 @@ export class InlineEditsSideBySideDiff extends Disposable { const left = Math.max(editorLayout.contentLeft, previewEditorLeft - horizontalScrollOffset); - const selectionTop = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); - const selectionBottom = this._editor.getTopForLineNumber(range.endLineNumberExclusive) - this._editorObs.scrollTop.read(reader); + const selectionTop = this._originalVerticalStartPosition.read(reader) ?? this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); + const selectionBottom = this._originalVerticalEndPosition.read(reader) ?? this._editor.getTopForLineNumber(range.endLineNumberExclusive) - this._editorObs.scrollTop.read(reader); const codeLeft = editorLayout.contentLeft; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index e350ade4807..a97c9fe15f5 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -7,20 +7,21 @@ import { h, isSVGElement } from '../../../../../../base/browser/dom.js'; import { KeybindingLabel, unthemedKeybindingLabelOptions } from '../../../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; import { numberComparator } from '../../../../../../base/common/arrays.js'; import { findFirstMin } from '../../../../../../base/common/arraysFind.js'; +import { BugIndicatingError } from '../../../../../../base/common/errors.js'; +import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; import { derived, IObservable, IReader } from '../../../../../../base/common/observable.js'; import { OS } from '../../../../../../base/common/platform.js'; -import { splitLines, getIndentationLength } from '../../../../../../base/common/strings.js'; +import { getIndentationLength, splitLines } from '../../../../../../base/common/strings.js'; import { URI } from '../../../../../../base/common/uri.js'; import { MenuEntryActionViewItem } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { Point } from '../../../../../browser/point.js'; import { LineRange } from '../../../../../common/core/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 { RangeMapping } from '../../../../../common/diff/rangeMapping.js'; -import { Range } from '../../../../../common/core/range.js'; -import { Position } from '../../../../../common/core/position.js'; -import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; -import { BugIndicatingError } from '../../../../../../base/common/errors.js'; export function maxContentWidthInRange(editor: ObservableCodeEditor, range: LineRange, reader: IReader): number { editor.layoutInfo.read(reader); @@ -82,25 +83,6 @@ export class StatusBarViewItem extends MenuEntryActionViewItem { } } -export class Point { - constructor( - public readonly x: number, - public readonly y: number, - ) { } - - public add(other: Point): Point { - return new Point(this.x + other.x, this.y + other.y); - } - - public deltaX(delta: number): Point { - return new Point(this.x + delta, this.y); - } - - public deltaY(delta: number): Point { - return new Point(this.x, this.y + delta); - } -} - export class UniqueUriGenerator { private static _modelId = 0; From 6cba7ebeed53ba13b1f4f3cd8c61c232262ecc45 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 Dec 2024 12:08:35 +0100 Subject: [PATCH 183/479] only extra-pad suggest header when also having doc (#236026) fixes https://github.com/microsoft/vscode-copilot/issues/10984 --- src/vs/editor/contrib/suggest/browser/media/suggest.css | 6 +++++- .../editor/contrib/suggest/browser/suggestWidgetDetails.ts | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/browser/media/suggest.css b/src/vs/editor/contrib/suggest/browser/media/suggest.css index e836d840e7b..755c457fc20 100644 --- a/src/vs/editor/contrib/suggest/browser/media/suggest.css +++ b/src/vs/editor/contrib/suggest/browser/media/suggest.css @@ -389,7 +389,11 @@ opacity: 0.7; white-space: pre; margin: 0 24px 0 0; - padding: 4px 0 12px 5px; + padding: 4px 0 4px 5px; +} + +.monaco-editor .suggest-details.detail-and-doc > .monaco-scrollable-element > .body > .header > .type { + padding-bottom: 12px; } .monaco-editor .suggest-details > .monaco-scrollable-element > .body > .header > .type.auto-wrap { diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts index ac2af6022b0..66101cdbd68 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts @@ -184,6 +184,8 @@ export class SuggestDetailsWidget { this._renderDisposeable.add(renderedContents); } + this.domNode.classList.toggle('detail-and-doc', !!detail && !!documentation); + this.domNode.style.userSelect = 'text'; this.domNode.tabIndex = -1; From 710a78d6d0cfb256f094cad4d29ea32111863615 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 13 Dec 2024 12:09:56 +0100 Subject: [PATCH 184/479] fix #233758 (#236027) --- .../extensions/browser/extensionEditor.ts | 289 ++++++++++-------- 1 file changed, 158 insertions(+), 131 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index e73432b6040..d5d8e0b3cd6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -275,9 +275,6 @@ export class ExtensionEditor extends EditorPane { @IViewsService private readonly viewsService: IViewsService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IHoverService private readonly hoverService: IHoverService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, - @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, - @IFileService private readonly fileService: IFileService, ) { super(ExtensionEditor.ID, group, telemetryService, themeService, storageService); this.extensionReadme = null; @@ -946,17 +943,162 @@ export class ExtensionEditor extends EditorPane { this.contentDisposables.add(toDisposable(removeLayoutParticipant)); this.contentDisposables.add(scrollableContent); - if (extension.local) { - this.renderInstallInfo(content, extension.local); + this.contentDisposables.add(this.instantiationService.createInstance(AdditionalDetailsWidget, content, extension)); + + append(container, scrollableContent.getDomNode()); + scrollableContent.scanDomNode(); + } + + private openChangelog(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { + return this.openMarkdown(extension, this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template.content, WebviewIndex.Changelog, localize('Changelog title', "Changelog"), token); + } + + private async openFeatures(template: IExtensionEditorTemplate, token: CancellationToken): Promise { + const manifest = await this.loadContents(() => this.extensionManifest!.get(), template.content); + if (token.isCancellationRequested) { + return null; } - if (extension.gallery) { - this.renderMarketplaceInfo(content, extension); + if (!manifest) { + return null; } - this.renderCategories(content, extension); - this.renderExtensionResources(content, extension); - append(container, scrollableContent.getDomNode()); + const extensionFeaturesTab = this.contentDisposables.add(this.instantiationService.createInstance(ExtensionFeaturesTab, manifest, (this.options)?.feature)); + const layout = () => extensionFeaturesTab.layout(template.content.clientHeight, template.content.clientWidth); + const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); + this.contentDisposables.add(toDisposable(removeLayoutParticipant)); + append(template.content, extensionFeaturesTab.domNode); + layout(); + return extensionFeaturesTab.domNode; + } + + private openExtensionDependencies(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return Promise.resolve(null); + } + + if (arrays.isFalsyOrEmpty(extension.dependencies)) { + append(template.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); + return Promise.resolve(template.content); + } + + const content = $('div', { class: 'subcontent' }); + const scrollableContent = new DomScrollableElement(content, {}); + append(template.content, scrollableContent.getDomNode()); + this.contentDisposables.add(scrollableContent); + + const dependenciesTree = this.instantiationService.createInstance(ExtensionsTree, + new ExtensionData(extension, null, extension => extension.dependencies || [], this.extensionsWorkbenchService), content, + { + listBackground: editorBackground + }); + const layout = () => { + scrollableContent.scanDomNode(); + const scrollDimensions = scrollableContent.getScrollDimensions(); + dependenciesTree.layout(scrollDimensions.height); + }; + const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); + this.contentDisposables.add(toDisposable(removeLayoutParticipant)); + + this.contentDisposables.add(dependenciesTree); + scrollableContent.scanDomNode(); + return Promise.resolve({ focus() { dependenciesTree.domFocus(); } }); + } + + private async openExtensionPack(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return Promise.resolve(null); + } + const manifest = await this.loadContents(() => this.extensionManifest!.get(), template.content); + if (token.isCancellationRequested) { + return null; + } + if (!manifest) { + return null; + } + return this.renderExtensionPack(manifest, template.content, token); + } + + private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return null; + } + + const content = $('div', { class: 'subcontent' }); + const scrollableContent = new DomScrollableElement(content, { useShadows: false }); + append(parent, scrollableContent.getDomNode()); + + const extensionsGridView = this.instantiationService.createInstance(ExtensionsGridView, content, new Delegate()); + const extensions: IExtension[] = await getExtensions(manifest.extensionPack!, this.extensionsWorkbenchService); + extensionsGridView.setExtensions(extensions); scrollableContent.scanDomNode(); + + this.contentDisposables.add(scrollableContent); + this.contentDisposables.add(extensionsGridView); + this.contentDisposables.add(toDisposable(arrays.insert(this.layoutParticipants, { layout: () => scrollableContent.scanDomNode() }))); + + return content; + } + + private loadContents(loadingTask: () => CacheResult, container: HTMLElement): Promise { + container.classList.add('loading'); + + const result = this.contentDisposables.add(loadingTask()); + const onDone = () => container.classList.remove('loading'); + result.promise.then(onDone, onDone); + + return result.promise; + } + + layout(dimension: Dimension): void { + this.dimension = dimension; + this.layoutParticipants.forEach(p => p.layout()); + } + + private onError(err: any): void { + if (isCancellationError(err)) { + return; + } + + this.notificationService.error(err); + } +} + +class AdditionalDetailsWidget extends Disposable { + + private readonly disposables = this._register(new DisposableStore()); + + constructor( + private readonly container: HTMLElement, + extension: IExtension, + @IHoverService private readonly hoverService: IHoverService, + @IOpenerService private readonly openerService: IOpenerService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IFileService private readonly fileService: IFileService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + ) { + super(); + this.render(extension); + this._register(this.extensionsWorkbenchService.onChange(e => { + if (e && areSameExtensions(e.identifier, extension.identifier) && e.server === extension.server) { + this.render(e); + } + })); + } + + private render(extension: IExtension): void { + this.container.innerText = ''; + this.disposables.clear(); + + if (extension.local) { + this.renderInstallInfo(this.container, extension.local); + } + if (extension.gallery) { + this.renderMarketplaceInfo(this.container, extension); + } + this.renderCategories(this.container, extension); + this.renderExtensionResources(this.container, extension); } private renderCategories(container: HTMLElement, extension: IExtension): void { @@ -965,7 +1107,7 @@ export class ExtensionEditor extends EditorPane { append(categoriesContainer, $('.additional-details-title', undefined, localize('categories', "Categories"))); const categoriesElement = append(categoriesContainer, $('.categories')); for (const category of extension.categories) { - this.transientDisposables.add(onClick(append(categoriesElement, $('span.category', { tabindex: '0' }, category)), + this.disposables.add(onClick(append(categoriesElement, $('span.category', { tabindex: '0' }, category)), () => this.extensionsWorkbenchService.openSearch(`@category:"${category}"`))); } } @@ -1000,10 +1142,8 @@ export class ExtensionEditor extends EditorPane { const resourcesElement = append(extensionResourcesContainer, $('.resources')); for (const [label, uri] of resources) { const resource = append(resourcesElement, $('a.resource', { tabindex: '0' }, label)); - this.transientDisposables.add(onClick(resource, () => { - this.openerService.open(uri); - })); - this.transientDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), resource, uri.toString())); + this.disposables.add(onClick(resource, () => this.openerService.open(uri))); + this.disposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), resource, uri.toString())); } } } @@ -1042,7 +1182,7 @@ export class ExtensionEditor extends EditorPane { if (isNative && extension.source === 'resource' && extension.location.scheme === Schemas.file) { element.classList.add('link'); element.title = extension.location.fsPath; - this.transientDisposables.add(onClick(element, () => this.openerService.open(extension.location, { openExternal: true }))); + this.disposables.add(onClick(element, () => this.openerService.open(extension.location, { openExternal: true }))); } } if (extension.size) { @@ -1056,7 +1196,7 @@ export class ExtensionEditor extends EditorPane { if (isNative && extension.location.scheme === Schemas.file) { element.classList.add('link'); element.title = extension.location.fsPath; - this.transientDisposables.add(onClick(element, () => this.openerService.open(extension.location, { openExternal: true }))); + this.disposables.add(onClick(element, () => this.openerService.open(extension.location, { openExternal: true }))); } } this.getCacheLocation(extension).then(cacheLocation => { @@ -1076,7 +1216,7 @@ export class ExtensionEditor extends EditorPane { if (isNative && extension.location.scheme === Schemas.file) { element.classList.add('link'); element.title = cacheLocation.fsPath; - this.transientDisposables.add(onClick(element, () => this.openerService.open(cacheLocation.with({ scheme: Schemas.file }), { openExternal: true }))); + this.disposables.add(onClick(element, () => this.openerService.open(cacheLocation.with({ scheme: Schemas.file }), { openExternal: true }))); } }); }); @@ -1125,119 +1265,6 @@ export class ExtensionEditor extends EditorPane { ); } } - - private openChangelog(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { - return this.openMarkdown(extension, this.extensionChangelog!.get(), localize('noChangelog', "No Changelog available."), template.content, WebviewIndex.Changelog, localize('Changelog title', "Changelog"), token); - } - - private async openFeatures(template: IExtensionEditorTemplate, token: CancellationToken): Promise { - const manifest = await this.loadContents(() => this.extensionManifest!.get(), template.content); - if (token.isCancellationRequested) { - return null; - } - if (!manifest) { - return null; - } - - const extensionFeaturesTab = this.contentDisposables.add(this.instantiationService.createInstance(ExtensionFeaturesTab, manifest, (this.options)?.feature)); - const layout = () => extensionFeaturesTab.layout(template.content.clientHeight, template.content.clientWidth); - const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); - this.contentDisposables.add(toDisposable(removeLayoutParticipant)); - append(template.content, extensionFeaturesTab.domNode); - layout(); - return extensionFeaturesTab.domNode; - } - - private openExtensionDependencies(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { - if (token.isCancellationRequested) { - return Promise.resolve(null); - } - - if (arrays.isFalsyOrEmpty(extension.dependencies)) { - append(template.content, $('p.nocontent')).textContent = localize('noDependencies', "No Dependencies"); - return Promise.resolve(template.content); - } - - const content = $('div', { class: 'subcontent' }); - const scrollableContent = new DomScrollableElement(content, {}); - append(template.content, scrollableContent.getDomNode()); - this.contentDisposables.add(scrollableContent); - - const dependenciesTree = this.instantiationService.createInstance(ExtensionsTree, - new ExtensionData(extension, null, extension => extension.dependencies || [], this.extensionsWorkbenchService), content, - { - listBackground: editorBackground - }); - const layout = () => { - scrollableContent.scanDomNode(); - const scrollDimensions = scrollableContent.getScrollDimensions(); - dependenciesTree.layout(scrollDimensions.height); - }; - const removeLayoutParticipant = arrays.insert(this.layoutParticipants, { layout }); - this.contentDisposables.add(toDisposable(removeLayoutParticipant)); - - this.contentDisposables.add(dependenciesTree); - scrollableContent.scanDomNode(); - return Promise.resolve({ focus() { dependenciesTree.domFocus(); } }); - } - - private async openExtensionPack(extension: IExtension, template: IExtensionEditorTemplate, token: CancellationToken): Promise { - if (token.isCancellationRequested) { - return Promise.resolve(null); - } - const manifest = await this.loadContents(() => this.extensionManifest!.get(), template.content); - if (token.isCancellationRequested) { - return null; - } - if (!manifest) { - return null; - } - return this.renderExtensionPack(manifest, template.content, token); - } - - private async renderExtensionPack(manifest: IExtensionManifest, parent: HTMLElement, token: CancellationToken): Promise { - if (token.isCancellationRequested) { - return null; - } - - const content = $('div', { class: 'subcontent' }); - const scrollableContent = new DomScrollableElement(content, { useShadows: false }); - append(parent, scrollableContent.getDomNode()); - - const extensionsGridView = this.instantiationService.createInstance(ExtensionsGridView, content, new Delegate()); - const extensions: IExtension[] = await getExtensions(manifest.extensionPack!, this.extensionsWorkbenchService); - extensionsGridView.setExtensions(extensions); - scrollableContent.scanDomNode(); - - this.contentDisposables.add(scrollableContent); - this.contentDisposables.add(extensionsGridView); - this.contentDisposables.add(toDisposable(arrays.insert(this.layoutParticipants, { layout: () => scrollableContent.scanDomNode() }))); - - return content; - } - - private loadContents(loadingTask: () => CacheResult, container: HTMLElement): Promise { - container.classList.add('loading'); - - const result = this.contentDisposables.add(loadingTask()); - const onDone = () => container.classList.remove('loading'); - result.promise.then(onDone, onDone); - - return result.promise; - } - - layout(dimension: Dimension): void { - this.dimension = dimension; - this.layoutParticipants.forEach(p => p.layout()); - } - - private onError(err: any): void { - if (isCancellationError(err)) { - return; - } - - this.notificationService.error(err); - } } const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', ExtensionEditor.ID), EditorContextKeys.focus.toNegated()); From 61a7a1a60ddd5345e7a468801296f759bbfe6c32 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:14:23 +0100 Subject: [PATCH 185/479] Add background hover feedback for panel title overflow (#236030) fixes #171077 --- .../workbench/browser/parts/media/paneCompositePart.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index a57bdd617ce..2b4518ca204 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -164,15 +164,15 @@ padding: 2px; } -.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label, -.monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label { +.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label:not(.codicon-more), +.monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item .action-label:not(.codicon-more) { border-radius: 0; } .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:not(.icon) .action-label, -.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .action-label.codicon, +.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .action-label.codicon:not(.codicon-more), .monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item:not(.icon) .action-label, -.monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .action-label.codicon { +.monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .action-label.codicon:not(.codicon-more) { background: none !important; } From 5325680bc687fcb98d4143c3a08965d8c64a539a Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:48:19 +0100 Subject: [PATCH 186/479] Align view and viewcontainer actions (#236033) fixes #230701 --- src/vs/base/browser/ui/splitview/paneview.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css index 62cb3fe642c..64587284592 100644 --- a/src/vs/base/browser/ui/splitview/paneview.css +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -57,6 +57,7 @@ .monaco-pane-view .pane > .pane-header > .actions { display: none; margin-left: auto; + margin-right: 8px; } .monaco-pane-view .pane > .pane-header > .actions .action-item { From 06c0c95e21fd33b7c929447c44496ad578845e90 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 13 Dec 2024 12:51:01 +0100 Subject: [PATCH 187/479] =?UTF-8?q?Offer=20Copilot=20setup=20from=20?= =?UTF-8?q?=F0=9F=A4=96=20even=20when=20extension=20is=20installed=20(fix?= =?UTF-8?q?=20microsoft/vscode-copilot#11278)=20(#236035)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/chat/browser/chatQuotasService.ts | 36 +------------------ .../contrib/chat/browser/chatSetup.ts | 28 ++++++++------- 2 files changed, 17 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index 51cee3588ca..62bf59aca5e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -9,14 +9,11 @@ import { language } from '../../../../base/common/platform.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { localize, localize2 } from '../../../../nls.js'; -import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +import { localize } from '../../../../nls.js'; import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { createDecorator, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; -import product from '../../../../platform/product/common/product.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; @@ -153,38 +150,7 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService } } - class SimulateCopilotQuotaExceeded extends Action2 { - constructor() { - super({ - id: 'workbench.action.chat.simulateCopilotQuotaExceeded', - title: localize2('simulateCopilotQuotaExceeded', "Simulate Copilot Quota Exceeded"), - f1: true, - category: Categories.Developer - }); - } - - override async run(accessor: ServicesAccessor): Promise { - const inputService = accessor.get(IQuickInputService); - const result = await inputService.pick([ - { label: 'Chat' }, - { label: 'Completions' } - ], { canPickMany: true, placeHolder: 'Pick the quotas to exceed' }); - - if (result) { - const resultSet = new Set(result.map(r => r.label)); - that.acceptQuotas({ - chatQuotaExceeded: resultSet.has('Chat'), - completionsQuotaExceeded: resultSet.has('Completions'), - quotaResetDate: new Date() - }); - } - } - } - registerAction2(ShowLimitReachedDialogAction); - if (product.quality !== 'stable') { - registerAction2(SimulateCopilotQuotaExceeded); - } } acceptQuotas(quotas: IChatQuotas): void { diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index a9b5eafe8eb..9f96bde75b4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -132,6 +132,13 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr private registerActions(): void { const that = this; + const chatSetupTriggerContext = ContextKeyExpr.and( + ContextKeyExpr.has('config.chat.experimental.offerSetup'), + ContextKeyExpr.or( + ChatContextKeys.Setup.installed.negate(), + ChatContextKeys.Setup.canSignUp + ) + ); class ChatSetupTriggerAction extends Action2 { constructor() { @@ -140,15 +147,12 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr title: TRIGGER_SETUP_COMMAND_LABEL, category: CHAT_CATEGORY, f1: true, - precondition: ContextKeyExpr.and( - ChatContextKeys.Setup.installed.negate(), - ContextKeyExpr.has('config.chat.experimental.offerSetup') - ), + precondition: chatSetupTriggerContext, menu: { id: MenuId.ChatCommandCenter, group: 'a_last', order: 1, - when: ChatContextKeys.Setup.installed.negate() + when: chatSetupTriggerContext } }); } @@ -227,17 +231,17 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr title: localize2('managePlan', "Upgrade to Copilot Pro"), category: localize2('chat.category', 'Chat'), f1: true, - precondition: ChatContextKeys.enabled, + precondition: ContextKeyExpr.or( + ChatContextKeys.Setup.canSignUp, + ChatContextKeys.Setup.limited, + ), menu: { id: MenuId.ChatCommandCenter, group: 'a_first', order: 1, - when: ContextKeyExpr.and( - ChatContextKeys.Setup.installed, - ContextKeyExpr.or( - ChatContextKeys.chatQuotaExceeded, - ChatContextKeys.completionsQuotaExceeded - ) + when: ContextKeyExpr.or( + ChatContextKeys.chatQuotaExceeded, + ChatContextKeys.completionsQuotaExceeded ) } }); From 7112b3b0957dc9129dafcf353324e27167cb3f22 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 13 Dec 2024 13:21:08 +0100 Subject: [PATCH 188/479] Make sure the preview editor is visible before trying to measure its line widths (#236041) --- .../browser/view/inlineEdits/sideBySideDiff.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 25b1d750fc4..c5ed83bd5cd 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -156,6 +156,8 @@ export class InlineEditsSideBySideDiff extends Disposable { this._editorContainerTopLeft.set(this._previewEditorLayoutInfo.map(i => i?.edit1), undefined); } + private readonly _display = derived(this, reader => !!this._uiState.read(reader) ? 'block' : 'none'); + private readonly previewRef = n.ref(); private readonly toolbarRef = n.ref(); @@ -258,6 +260,18 @@ export class InlineEditsSideBySideDiff extends Disposable { private readonly _updatePreviewEditor = derived(reader => { this._editorContainer.readEffect(reader); + // Setting this here explicitly to make sure that the preview editor is + // visible when needed, we're also checking that these fields are defined + // because of the auto run initial + // Before removing these, verify with a non-monospace font family + this._display.read(reader); + if (this._overflowView) { + this._overflowView.element.style.display = this._display.read(reader); + } + if (this._nonOverflowView) { + this._nonOverflowView.element.style.display = this._display.read(reader); + } + const uiState = this._uiState.read(reader); if (!uiState) { return; @@ -513,8 +527,6 @@ export class InlineEditsSideBySideDiff extends Disposable { ]; })).keepUpdated(this._store); - private readonly _display = derived(this, reader => !!this._uiState.read(reader) ? 'block' : 'none'); - private readonly _nonOverflowView = n.div({ class: 'inline-edits-view', style: { From fa094097da415cd7d91241d6a53aa622360b206c Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 13 Dec 2024 13:27:42 +0100 Subject: [PATCH 189/479] Revert "Fix custom task shell doesn't work without manually passing in "run command" arg/flag (#181760)" (#236043) This reverts commit fc3cc5b1c6eb2f56a8ff194cef8844da393652cf. --- .../tasks/browser/terminalTaskSystem.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 45a7b5d022c..045a9e63b44 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1111,6 +1111,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { color: task.configurationProperties.icon?.color || undefined, waitOnExit }; + let shellSpecified: boolean = false; const shellOptions: IShellConfiguration | undefined = task.command.options && task.command.options.shell; if (shellOptions) { if (shellOptions.executable) { @@ -1119,12 +1120,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig.args = undefined; } shellLaunchConfig.executable = await this._resolveVariable(variableResolver, shellOptions.executable); + shellSpecified = true; } if (shellOptions.args) { shellLaunchConfig.args = await this._resolveVariables(variableResolver, shellOptions.args.slice()); } } - const shellArgsSpecified: boolean = shellLaunchConfig.args !== undefined; if (shellLaunchConfig.args === undefined) { shellLaunchConfig.args = []; } @@ -1137,29 +1138,29 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { windowsShellArgs = true; // If we don't have a cwd, then the terminal uses the home dir. const userHome = await this._pathService.userHome(); - if (basename === 'cmd.exe') { - if ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath))) { - return undefined; - } - if (!shellArgsSpecified) { - toAdd.push('/d', '/c'); - } - } else if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { - if (!shellArgsSpecified) { + if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { + return undefined; + } + if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { + if (!shellSpecified) { toAdd.push('-Command'); } } else if ((basename === 'bash.exe') || (basename === 'zsh.exe')) { windowsShellArgs = false; - if (!shellArgsSpecified) { + if (!shellSpecified) { toAdd.push('-c'); } } else if (basename === 'wsl.exe') { - if (!shellArgsSpecified) { + if (!shellSpecified) { toAdd.push('-e'); } + } else { + if (!shellSpecified) { + toAdd.push('/d', '/c'); + } } } else { - if (!shellArgsSpecified) { + if (!shellSpecified) { // Under Mac remove -l to not start it as a login shell. if (platform === Platform.Platform.Mac) { // Background on -l on osx https://github.com/microsoft/vscode/issues/107563 @@ -1266,12 +1267,11 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const combinedShellArgs: string[] = Objects.deepClone(configuredShellArgs); shellCommandArgs.forEach(element => { const shouldAddShellCommandArg = configuredShellArgs.every((arg, index) => { - const isDuplicated = arg.toLowerCase() === element.toLowerCase(); - if (isDuplicated && (configuredShellArgs.length > index + 1)) { + if ((arg.toLowerCase() === element) && (configuredShellArgs.length > index + 1)) { // We can still add the argument, but only if not all of the following arguments begin with "-". return !configuredShellArgs.slice(index + 1).every(testArg => testArg.startsWith('-')); } else { - return !isDuplicated; + return arg.toLowerCase() !== element; } }); if (shouldAddShellCommandArg) { From 15bd01ac9f76cd053a9edd4aeff0be056542a67c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 Dec 2024 13:28:57 +0100 Subject: [PATCH 190/479] fix https://github.com/microsoft/vscode/issues/224944 (#236034) --- .../contrib/callHierarchy/common/callHierarchy.ts | 4 ++-- .../contrib/typeHierarchy/common/typeHierarchy.ts | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts index c0ea84d13a7..795a96fd018 100644 --- a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts @@ -175,7 +175,7 @@ CommandsRegistry.registerCommand('_executeProvideIncomingCalls', async (_accesso // find model const model = _models.get(item._sessionId); if (!model) { - return undefined; + return []; } return model.resolveIncomingCalls(item, CancellationToken.None); @@ -188,7 +188,7 @@ CommandsRegistry.registerCommand('_executeProvideOutgoingCalls', async (_accesso // find model const model = _models.get(item._sessionId); if (!model) { - return undefined; + return []; } return model.resolveOutgoingCalls(item, CancellationToken.None); diff --git a/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts b/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts index 71f4f44687e..3144b4c7a99 100644 --- a/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts +++ b/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts @@ -138,14 +138,18 @@ CommandsRegistry.registerCommand('_executePrepareTypeHierarchy', async (accessor return []; } - _models.set(model.id, model); _models.forEach((value, key, map) => { if (map.size > 10) { value.dispose(); _models.delete(key); } }); - return [model.root]; + + for (const root of model.roots) { + _models.set(root._sessionId, model); + } + + return model.roots; } finally { textModelReference?.dispose(); @@ -169,7 +173,7 @@ CommandsRegistry.registerCommand('_executeProvideSupertypes', async (_accessor, // find model const model = _models.get(item._sessionId); if (!model) { - return undefined; + return []; } return model.provideSupertypes(item, CancellationToken.None); @@ -182,7 +186,7 @@ CommandsRegistry.registerCommand('_executeProvideSubtypes', async (_accessor, .. // find model const model = _models.get(item._sessionId); if (!model) { - return undefined; + return []; } return model.provideSubtypes(item, CancellationToken.None); From 21a129e8d0fbdfabcae2444b04a0e4c3912c6fe1 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 13 Dec 2024 13:33:34 +0100 Subject: [PATCH 191/479] Revert "Render expanded hovers always on the same side, so does not jump around" (#236044) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Render expanded hovers always on the same side, so does not jump arou…" This reverts commit 82d1ef937686fcb7bca342410f9071f84fbdb3a1. --- .../editor/contrib/hover/browser/contentHoverRendered.ts | 2 +- src/vs/editor/contrib/hover/browser/contentHoverWidget.ts | 6 ++---- .../contrib/hover/browser/contentHoverWidgetWrapper.ts | 6 +++--- src/vs/editor/contrib/hover/browser/hoverTypes.ts | 6 +----- .../contrib/hover/browser/markdownHoverParticipant.ts | 8 ++++---- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index 32d72a6693e..81ccfcbc60b 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -408,7 +408,7 @@ class RenderedContentHoverParts extends Disposable { this._context.focus(); } } - this._context.onContentsChanged({ allowPositionPreferenceRecomputation: false }); + this._context.onContentsChanged(); } public doesHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts index 063e4d108fe..ffb8aaa9f11 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts @@ -17,7 +17,6 @@ import { getHoverAccessibleViewHint, HoverWidget } from '../../../../base/browse import { PositionAffinity } from '../../../common/model.js'; import { Emitter } from '../../../../base/common/event.js'; import { RenderedContentHover } from './contentHoverRendered.js'; -import { IContentsChangeOptions } from './hoverTypes.js'; const HORIZONTAL_SCROLLING_BY = 30; @@ -393,7 +392,7 @@ export class ContentHoverWidget extends ResizableContentWidget { this._resizableNode.minSize = new dom.Dimension(width, this._minimumSize.height); } - public onContentsChanged(opts: IContentsChangeOptions = { allowPositionPreferenceRecomputation: true }): void { + public onContentsChanged(): void { this._removeConstraintsRenderNormally(); const contentsDomNode = this._hover.contentsDomNode; @@ -408,9 +407,8 @@ export class ContentHoverWidget extends ResizableContentWidget { this._contentWidth = width; this._updateMinimumWidth(); this._resizableNode.layout(height, width); - this._updateResizableNodeMaxDimensions(); - if (opts.allowPositionPreferenceRecomputation && this._renderedHover?.showAtPosition) { + if (this._renderedHover?.showAtPosition) { const widgetHeight = dom.getTotalHeight(this._hover.containerDomNode); this._positionPreference = this._findPositionPreference(widgetHeight, this._renderedHover.showAtPosition); } diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts index d8439510336..ad9a9920967 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts @@ -11,7 +11,7 @@ import { EditorOption } from '../../../common/config/editorOptions.js'; import { Range } from '../../../common/core/range.js'; import { TokenizationRegistry } from '../../../common/languages.js'; import { HoverOperation, HoverResult, HoverStartMode, HoverStartSource } from './hoverOperation.js'; -import { HoverAnchor, HoverParticipantRegistry, HoverRangeAnchor, IContentsChangeOptions, IEditorHoverContext, IEditorHoverParticipant, IHoverPart, IHoverWidget } from './hoverTypes.js'; +import { HoverAnchor, HoverParticipantRegistry, HoverRangeAnchor, IEditorHoverContext, IEditorHoverParticipant, IHoverPart, IHoverWidget } from './hoverTypes.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { HoverVerbosityAction } from '../../../common/standalone/standaloneEnums.js'; @@ -224,9 +224,9 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge const hide = () => { this.hide(); }; - const onContentsChanged = (opts: IContentsChangeOptions) => { + const onContentsChanged = () => { this._onContentsChanged.fire(); - this._contentHoverWidget.onContentsChanged(opts); + this._contentHoverWidget.onContentsChanged(); }; const setMinimumDimensions = (dimensions: dom.Dimension) => { this._contentHoverWidget.setMinimumDimensions(dimensions); diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index babdf0f5556..ad5442f1fa2 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -95,15 +95,11 @@ export interface IEditorHoverColorPickerWidget { layout(): void; } -export interface IContentsChangeOptions { - allowPositionPreferenceRecomputation: boolean; -} - export interface IEditorHoverContext { /** * The contents rendered inside the fragment have been changed, which means that the hover should relayout. */ - onContentsChanged(opts?: IContentsChangeOptions): void; + onContentsChanged(): void; /** * Set the minimum dimensions of the resizable hover */ diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index e98b45195e2..3fd5876cfe4 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -15,7 +15,7 @@ import { Position } from '../../../common/core/position.js'; import { Range } from '../../../common/core/range.js'; import { IModelDecoration, ITextModel } from '../../../common/model.js'; import { ILanguageService } from '../../../common/languages/language.js'; -import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IContentsChangeOptions, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from './hoverTypes.js'; +import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from './hoverTypes.js'; import * as nls from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; @@ -245,9 +245,9 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { private readonly _keybindingService: IKeybindingService, private readonly _hoverService: IHoverService, private readonly _configurationService: IConfigurationService, - private readonly onContentsChanged: (opts?: IContentsChangeOptions | undefined) => void, + private readonly _onFinishedRendering: () => void, ) { - this.renderedHoverParts = this._renderHoverParts(hoverParts, hoverPartsContainer, this.onContentsChanged); + this.renderedHoverParts = this._renderHoverParts(hoverParts, hoverPartsContainer, this._onFinishedRendering); this._disposables.add(toDisposable(() => { this.renderedHoverParts.forEach(renderedHoverPart => { renderedHoverPart.dispose(); @@ -416,7 +416,7 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { if (index >= this.renderedHoverParts.length || index < 0) { return undefined; } - const renderedHoverPart = this._renderHoverPart(hoverPart, () => this.onContentsChanged({ allowPositionPreferenceRecomputation: false })); + const renderedHoverPart = this._renderHoverPart(hoverPart, this._onFinishedRendering); const currentRenderedHoverPart = this.renderedHoverParts[index]; const currentRenderedMarkdown = currentRenderedHoverPart.hoverElement; const renderedMarkdown = renderedHoverPart.hoverElement; From c168b55cc85bc38a8db9b434aff8b58026c70fda Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 13 Dec 2024 13:35:48 +0100 Subject: [PATCH 192/479] Install Copilot extension for all profiles (fix microsoft/vscode-copilot#11256) (#236042) --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 9f96bde75b4..2245da971d3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -834,8 +834,9 @@ class ChatSetupController extends Disposable { await this.extensionsWorkbenchService.install(defaultChat.extensionId, { enable: true, - isMachineScoped: false, - installEverywhere: true, + isApplicationScoped: true, // install into all profiles + isMachineScoped: false, // do not ask to sync + installEverywhere: true, // install in local and remote installPreReleaseVersion: this.productService.quality !== 'stable' }, isCopilotEditsViewActive(this.viewsService) ? EditsViewId : ChatViewId); From 1590668ba713bdefd82f5f805735be1c96020f4d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:22:40 +0100 Subject: [PATCH 193/479] Git - add the ability to continue merge/rebase if there are no changes (#236045) --- extensions/git/src/actionButton.ts | 34 +++++++++++++++++++++++++----- extensions/git/src/commands.ts | 10 ++++++--- extensions/git/src/git.ts | 10 +++++++++ extensions/git/src/operation.ts | 11 ++++++---- extensions/git/src/repository.ts | 17 +++++++++++++-- 5 files changed, 68 insertions(+), 14 deletions(-) diff --git a/extensions/git/src/actionButton.ts b/extensions/git/src/actionButton.ts index d801fbba05f..0c94636fff2 100644 --- a/extensions/git/src/actionButton.ts +++ b/extensions/git/src/actionButton.ts @@ -25,7 +25,8 @@ function isActionButtonStateEqual(state1: ActionButtonState, state2: ActionButto state1.isMergeInProgress === state2.isMergeInProgress && state1.isRebaseInProgress === state2.isRebaseInProgress && state1.isSyncInProgress === state2.isSyncInProgress && - state1.repositoryHasChangesToCommit === state2.repositoryHasChangesToCommit; + state1.repositoryHasChangesToCommit === state2.repositoryHasChangesToCommit && + state1.repositoryHasUnresolvedConflicts === state2.repositoryHasUnresolvedConflicts; } interface ActionButtonState { @@ -36,6 +37,7 @@ interface ActionButtonState { readonly isRebaseInProgress: boolean; readonly isSyncInProgress: boolean; readonly repositoryHasChangesToCommit: boolean; + readonly repositoryHasUnresolvedConflicts: boolean; } export class ActionButton { @@ -68,7 +70,8 @@ export class ActionButton { isMergeInProgress: false, isRebaseInProgress: false, isSyncInProgress: false, - repositoryHasChangesToCommit: false + repositoryHasChangesToCommit: false, + repositoryHasUnresolvedConflicts: false }; repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables); @@ -128,7 +131,11 @@ export class ActionButton { return { command: primaryCommand, secondaryCommands: this.getCommitActionButtonSecondaryCommands(), - enabled: (this.state.repositoryHasChangesToCommit || this.state.isRebaseInProgress) && !this.state.isCommitInProgress && !this.state.isMergeInProgress + enabled: ( + this.state.repositoryHasChangesToCommit || + (this.state.isRebaseInProgress && !this.state.repositoryHasUnresolvedConflicts) || + (this.state.isMergeInProgress && !this.state.repositoryHasUnresolvedConflicts)) && + !this.state.isCommitInProgress }; } @@ -143,6 +150,16 @@ export class ActionButton { }; } + // Merge Continue + if (this.state.isMergeInProgress) { + return { + command: 'git.commit', + title: l10n.t('{0} Continue', '$(check)'), + tooltip: this.state.isCommitInProgress ? l10n.t('Continuing Merge...') : l10n.t('Continue Merge'), + arguments: [this.repository.sourceControl, null] + }; + } + // Not a branch (tag, detached) if (this.state.HEAD?.type === RefType.Tag || !this.state.HEAD?.name) { return { @@ -163,6 +180,11 @@ export class ActionButton { return []; } + // Merge Continue + if (this.state.isMergeInProgress) { + return []; + } + // Not a branch (tag, detached) if (this.state.HEAD?.type === RefType.Tag || !this.state.HEAD?.name) { return []; @@ -240,6 +262,7 @@ export class ActionButton { const isCommitInProgress = this.repository.operations.isRunning(OperationKind.Commit) || this.repository.operations.isRunning(OperationKind.PostCommitCommand) || + this.repository.operations.isRunning(OperationKind.MergeContinue) || this.repository.operations.isRunning(OperationKind.RebaseContinue); const isSyncInProgress = @@ -261,9 +284,10 @@ export class ActionButton { this.state = { ...this.state, HEAD: this.repository.HEAD, - isMergeInProgress: this.repository.mergeGroup.resourceStates.length !== 0, + isMergeInProgress: this.repository.mergeInProgress, isRebaseInProgress: !!this.repository.rebaseCommit, - repositoryHasChangesToCommit: this.repositoryHasChangesToCommit() + repositoryHasChangesToCommit: this.repositoryHasChangesToCommit(), + repositoryHasUnresolvedConflicts: this.repository.mergeGroup.resourceStates.length > 0 }; } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 5266955198f..f93340d928e 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2177,6 +2177,8 @@ export class CommandCenter { // amend allows changing only the commit message && !opts.amend && !opts.empty + // merge not in progress + && !repository.mergeInProgress // rebase not in progress && repository.rebaseCommit === undefined ) { @@ -4418,15 +4420,17 @@ export class CommandCenter { message = l10n.t('Can\'t force push refs to remote. The tip of the remote-tracking branch has been updated since the last checkout. Try running "Pull" first to pull the latest changes from the remote branch first.'); break; case GitErrorCodes.Conflict: - message = l10n.t('There are merge conflicts. Resolve them before committing.'); + message = l10n.t('There are merge conflicts. Please resolve them before committing your changes.'); type = 'warning'; + choices.clear(); choices.set(l10n.t('Show Changes'), () => commands.executeCommand('workbench.view.scm')); options.modal = false; break; case GitErrorCodes.StashConflict: - message = l10n.t('There were merge conflicts while applying the stash.'); - choices.set(l10n.t('Show Changes'), () => commands.executeCommand('workbench.view.scm')); + message = l10n.t('There are merge conflicts while applying the stash. Please resolve them before committing your changes.'); type = 'warning'; + choices.clear(); + choices.set(l10n.t('Show Changes'), () => commands.executeCommand('workbench.view.scm')); options.modal = false; break; case GitErrorCodes.AuthenticationFailed: { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index de8b503ce54..4a968792a77 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1880,6 +1880,16 @@ export class Repository { await this.exec(['merge', '--abort']); } + async mergeContinue(): Promise { + const args = ['merge', '--continue']; + + try { + await this.exec(args, { env: { GIT_EDITOR: 'true' } }); + } catch (commitErr) { + await this.handleCommitError(commitErr); + } + } + async tag(options: { name: string; message?: string; ref?: string }): Promise { let args = ['tag']; diff --git a/extensions/git/src/operation.ts b/extensions/git/src/operation.ts index 6f3f0a1fa68..65a932f7702 100644 --- a/extensions/git/src/operation.ts +++ b/extensions/git/src/operation.ts @@ -39,6 +39,7 @@ export const enum OperationKind { Merge = 'Merge', MergeAbort = 'MergeAbort', MergeBase = 'MergeBase', + MergeContinue = 'MergeContinue', Move = 'Move', PostCommitCommand = 'PostCommitCommand', Pull = 'Pull', @@ -69,10 +70,10 @@ export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchO DeleteRefOperation | DeleteRemoteTagOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation | GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetObjectFilesOperation | GetRefsOperation | GetRemoteRefsOperation | HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | - MergeBaseOperation | MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | - RemoveOperation | ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RefreshOperation | RevertFilesOperation | - RevListOperation | RevParseOperation | SetBranchUpstreamOperation | ShowOperation | StageOperation | StatusOperation | StashOperation | - SubmoduleUpdateOperation | SyncOperation | TagOperation; + MergeBaseOperation | MergeContinueOperation | MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | + RenameBranchOperation | RemoveOperation | ResetOperation | RebaseOperation | RebaseAbortOperation | RebaseContinueOperation | RefreshOperation | + RevertFilesOperation | RevListOperation | RevParseOperation | SetBranchUpstreamOperation | ShowOperation | StageOperation | StatusOperation | + StashOperation | SubmoduleUpdateOperation | SyncOperation | TagOperation; type BaseOperation = { kind: OperationKind; blocking: boolean; readOnly: boolean; remote: boolean; retry: boolean; showProgress: boolean }; export type AddOperation = BaseOperation & { kind: OperationKind.Add }; @@ -107,6 +108,7 @@ export type LogFileOperation = BaseOperation & { kind: OperationKind.LogFile }; export type MergeOperation = BaseOperation & { kind: OperationKind.Merge }; export type MergeAbortOperation = BaseOperation & { kind: OperationKind.MergeAbort }; export type MergeBaseOperation = BaseOperation & { kind: OperationKind.MergeBase }; +export type MergeContinueOperation = BaseOperation & { kind: OperationKind.MergeContinue }; export type MoveOperation = BaseOperation & { kind: OperationKind.Move }; export type PostCommitCommandOperation = BaseOperation & { kind: OperationKind.PostCommitCommand }; export type PullOperation = BaseOperation & { kind: OperationKind.Pull }; @@ -164,6 +166,7 @@ export const Operation = { Merge: { kind: OperationKind.Merge, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as MergeOperation, MergeAbort: { kind: OperationKind.MergeAbort, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as MergeAbortOperation, MergeBase: { kind: OperationKind.MergeBase, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as MergeBaseOperation, + MergeContinue: { kind: OperationKind.MergeContinue, blocking: true, readOnly: false, remote: false, retry: false, showProgress: true } as MergeAbortOperation, Move: { kind: OperationKind.Move, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as MoveOperation, PostCommitCommand: { kind: OperationKind.PostCommitCommand, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as PostCommitCommandOperation, Pull: { kind: OperationKind.Pull, blocking: true, readOnly: false, remote: true, retry: true, showProgress: true } as PullOperation, diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ef7997c7542..e3b924d5868 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1251,7 +1251,20 @@ export class Repository implements Disposable { const workingGroupResources = opts.all && opts.all !== 'tracked' ? [...this.workingTreeGroup.resourceStates.map(r => r.resourceUri.fsPath)] : []; - if (this.rebaseCommit) { + if (this.mergeInProgress) { + await this.run( + Operation.MergeContinue, + async () => { + if (opts.all) { + const addOpts = opts.all === 'tracked' ? { update: true } : {}; + await this.repository.add([], addOpts); + } + + await this.repository.mergeContinue(); + await this.commitOperationCleanup(message, indexResources, workingGroupResources); + }, + () => this.commitOperationGetOptimisticResourceGroups(opts)); + } else if (this.rebaseCommit) { await this.run( Operation.RebaseContinue, async () => { @@ -2521,7 +2534,7 @@ export class Repository implements Disposable { return head + (this.workingTreeGroup.resourceStates.length + this.untrackedGroup.resourceStates.length > 0 ? '*' : '') + (this.indexGroup.resourceStates.length > 0 ? '+' : '') - + (this.mergeGroup.resourceStates.length > 0 ? '!' : ''); + + (this.mergeInProgress || !!this.rebaseCommit ? '!' : ''); } get syncLabel(): string { From af63c536164e34b42b317f11afae9da6433e2d92 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 13 Dec 2024 14:25:17 +0100 Subject: [PATCH 194/479] Adds InlineCompletionContext.requestUuid (#236046) --- src/vs/editor/common/languages.ts | 5 +++++ .../browser/model/provideInlineCompletions.ts | 9 ++++++--- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 4 +++- .../vscode.proposed.inlineCompletionsAdditions.d.ts | 2 ++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 9d97fb451a8..0c35ac40fcc 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -703,6 +703,11 @@ export interface InlineCompletionContext { * @internal */ readonly userPrompt?: string | undefined; + /** + * @experimental + * @internal + */ + readonly requestUuid?: string | undefined; readonly includeInlineEdits: boolean; readonly includeInlineCompletions: boolean; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts index 91c047eff9d..befcccca81a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts @@ -9,6 +9,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../../base/ import { onUnexpectedExternalError } from '../../../../../base/common/errors.js'; import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; import { SetMap } from '../../../../../base/common/map.js'; +import { generateUuid } from '../../../../../base/common/uuid.js'; import { ISingleEditOperation } from '../../../../common/core/editOperation.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Position } from '../../../../common/core/position.js'; @@ -32,8 +33,10 @@ export async function provideInlineCompletions( baseToken: CancellationToken = CancellationToken.None, languageConfigurationService?: ILanguageConfigurationService, ): Promise { + const requestUuid = generateUuid(); const tokenSource = new CancellationTokenSource(baseToken); const token = tokenSource.token; + const contextWithUuid = { ...context, requestUuid: requestUuid }; const defaultReplaceRange = positionOrRange instanceof Position ? getDefaultRange(positionOrRange, model) : positionOrRange; const providers = registry.all(model); @@ -116,9 +119,9 @@ export async function provideInlineCompletions( let result: InlineCompletions | null | undefined; try { if (positionOrRange instanceof Position) { - result = await provider.provideInlineCompletions(model, positionOrRange, context, token); + result = await provider.provideInlineCompletions(model, positionOrRange, contextWithUuid, token); } else { - result = await provider.provideInlineEditsForRange?.(model, positionOrRange, context, token); + result = await provider.provideInlineEditsForRange?.(model, positionOrRange, contextWithUuid, token); } } catch (e) { onUnexpectedExternalError(e); @@ -140,7 +143,7 @@ export async function provideInlineCompletions( return new InlineCompletionProviderResult([], new Set(), []); } - const result = await addRefAndCreateResult(context, inlineCompletionLists, defaultReplaceRange, model, languageConfigurationService); + const result = await addRefAndCreateResult(contextWithUuid, inlineCompletionLists, defaultReplaceRange, model, languageConfigurationService); tokenSource.dispose(true); // This disposes results that are not referenced. return result; } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index c39b9347e70..cd42bd3bf77 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1345,7 +1345,8 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { text: context.selectedSuggestionInfo.text } : undefined, - triggerKind: this.languageTriggerKindToVSCodeTriggerKind[context.triggerKind] + triggerKind: this.languageTriggerKindToVSCodeTriggerKind[context.triggerKind], + requestUuid: context.requestUuid, }, token); if (!result) { @@ -1422,6 +1423,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { : undefined, triggerKind: this.languageTriggerKindToVSCodeTriggerKind[context.triggerKind], userPrompt: context.userPrompt, + requestUuid: context.requestUuid, }, token); if (!result) { diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts index 8a71a374846..b207361af4a 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts @@ -66,6 +66,8 @@ declare module 'vscode' { export interface InlineCompletionContext { readonly userPrompt?: string; + + readonly requestUuid?: string; } export interface PartialAcceptInfo { From af33df91a45498435bc47f16444d91db4582ce48 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 13 Dec 2024 14:37:02 +0100 Subject: [PATCH 195/479] fix #141663 (#236050) --- src/vs/platform/userDataSync/common/userDataSyncMachines.ts | 5 ++++- .../contrib/userDataSync/browser/userDataSyncViews.ts | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts index 1c36206421a..c91c8447973 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts @@ -151,7 +151,10 @@ export class UserDataSyncMachinesService extends Disposable implements IUserData private computeCurrentMachineName(machines: IMachineData[]): string { const previousName = this.storageService.get(currentMachineNameKey, StorageScope.APPLICATION); if (previousName) { - return previousName; + if (!machines.some(machine => machine.name === previousName)) { + return previousName; + } + this.storageService.remove(currentMachineNameKey, StorageScope.APPLICATION); } const namePrefix = `${this.productService.embedderIdentifier ? `${this.productService.embedderIdentifier} - ` : ''}${getPlatformName()} (${this.productService.nameShort})`; diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index 082c55bda66..8ced2a6fa5d 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -697,6 +697,7 @@ class UserDataSyncMachinesViewDataProvider implements ITreeViewDataProvider { inputBox.show(); const machines = await this.getMachines(); const machine = machines.find(({ id }) => id === machineId); + const enabledMachines = machines.filter(({ disabled }) => !disabled); if (!machine) { inputBox.hide(); disposableStore.dispose(); @@ -706,7 +707,7 @@ class UserDataSyncMachinesViewDataProvider implements ITreeViewDataProvider { inputBox.value = machine.name; const validateMachineName = (machineName: string): string | null => { machineName = machineName.trim(); - return machineName && !machines.some(m => m.id !== machineId && m.name === machineName) ? machineName : null; + return machineName && !enabledMachines.some(m => m.id !== machineId && m.name === machineName) ? machineName : null; }; disposableStore.add(inputBox.onDidChangeValue(() => inputBox.validationMessage = validateMachineName(inputBox.value) ? '' : localize('valid message', "Machine name should be unique and not empty"))); From 380de2f146b207d3ea9dce33dec1e2fe8aba0b5a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:48:37 +0100 Subject: [PATCH 196/479] Git - render emojis when showing blame information (#236051) --- extensions/git/src/blame.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index 04b90e0725e..9bb740fdeb2 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -10,6 +10,7 @@ import { Repository } from './repository'; import { throttle } from './decorators'; import { BlameInformation } from './git'; import { fromGitUri, isGitUri } from './uri'; +import { emojify, ensureEmojis } from './emoji'; function lineRangesContainLine(changes: readonly TextEditorChange[], lineNumber: number): boolean { return changes.some(c => c.modified.startLineNumber <= lineNumber && lineNumber < c.modified.endLineNumberExclusive); @@ -171,7 +172,7 @@ export class GitBlameController { const templateTokens = { hash: blameInformation.hash, hashShort: blameInformation.hash.substring(0, 8), - subject: blameInformation.subject ?? '', + subject: emojify(blameInformation.subject ?? ''), authorName: blameInformation.authorName ?? '', authorEmail: blameInformation.authorEmail ?? '', authorDate: new Date(blameInformation.authorDate ?? new Date()).toLocaleString(), @@ -203,7 +204,7 @@ export class GitBlameController { markdownString.appendMarkdown('\n\n'); } - markdownString.appendMarkdown(`${blameInformation.subject}\n\n`); + markdownString.appendMarkdown(`${emojify(blameInformation.subject ?? '')}\n\n`); markdownString.appendMarkdown(`---\n\n`); markdownString.appendMarkdown(`[$(eye) View Commit](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformation.hash]))} "${l10n.t('View Commit')}")`); @@ -259,6 +260,10 @@ export class GitBlameController { return resourceBlameInformation; } + // Ensure that the emojis are loaded. We will + // use them when formatting the blame information. + await ensureEmojis(); + // Get blame information for the resource and cache it const blameInformation = await repository.blame2(resource.fsPath, commit) ?? []; this._repositoryBlameCache.setBlameInformation(repository, resource, commit, blameInformation); From 771c5177d3355542b8db71541996d7748371fe3d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:54:30 +0100 Subject: [PATCH 197/479] Git - fix `when` clause for keybindings (#236053) --- extensions/git/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 81c825251a4..9aedfbb16e3 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -943,7 +943,7 @@ "command": "git.stageSelectedRanges", "key": "ctrl+k ctrl+alt+s", "mac": "cmd+k cmd+alt+s", - "when": "resourceScheme =~ /^git$|^file$/" + "when": "editorTextFocus && resourceScheme =~ /^git$|^file$/" }, { "command": "git.unstageSelectedRanges", @@ -955,7 +955,7 @@ "command": "git.revertSelectedRanges", "key": "ctrl+k ctrl+r", "mac": "cmd+k cmd+r", - "when": "resourceScheme =~ /^git$|^file$/" + "when": "editorTextFocus && resourceScheme =~ /^git$|^file$/" } ], "menus": { From 13a37df8910390b7aa5692a01fd0e01079b318b1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 Dec 2024 15:29:22 +0100 Subject: [PATCH 198/479] add error logging for https://github.com/microsoft/vscode/issues/227147 (#236054) --- .../test/common/ternarySearchtree.test.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/vs/base/test/common/ternarySearchtree.test.ts b/src/vs/base/test/common/ternarySearchtree.test.ts index dcb2771d27e..592ef0a597c 100644 --- a/src/vs/base/test/common/ternarySearchtree.test.ts +++ b/src/vs/base/test/common/ternarySearchtree.test.ts @@ -536,14 +536,18 @@ suite('Ternary Search Tree', () => { } const tst = TernarySearchTree.forUris(); - for (const item of keys) { - tst.set(item, true); - assert.ok(tst._isBalanced(), `SET${item}|${keys.map(String).join()}`); - } - - for (const item of keys) { - tst.delete(item); - assert.ok(tst._isBalanced(), `DEL${item}|${keys.map(String).join()}`); + try { + for (const item of keys) { + tst.set(item, true); + assert.ok(tst._isBalanced(), `SET${item}|${keys.map(String).join()}`); + } + + for (const item of keys) { + tst.delete(item); + assert.ok(tst._isBalanced(), `DEL${item}|${keys.map(String).join()}`); + } + } catch (err) { + assert.ok(false, `FAILED with keys: ${keys.map(String).join()}`); } } }); From 12b060f0a812fcd7e4cabf9c65e8267f48519459 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:33:25 +0100 Subject: [PATCH 199/479] Git - fix bug related to toggling git decorations (#172637) (#236055) --- extensions/git/src/decorationProvider.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 548f3aa2a1c..ddb3574890b 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -273,6 +273,7 @@ class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider export class GitDecorations { + private enabled = false; private disposables: Disposable[] = []; private modelDisposables: Disposable[] = []; private providers = new Map(); @@ -286,13 +287,19 @@ export class GitDecorations { } private update(): void { - const enabled = workspace.getConfiguration('git').get('decorations.enabled'); + const config = workspace.getConfiguration('git'); + const enabled = config.get('decorations.enabled') === true; + if (this.enabled === enabled) { + return; + } if (enabled) { this.enable(); } else { this.disable(); } + + this.enabled = enabled; } private enable(): void { From ab7dae30bfe722a3571fb82a55b61359c45a5174 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 Dec 2024 15:44:39 +0100 Subject: [PATCH 200/479] cancel session when token is cancelled (#236056) https://github.com/microsoft/vscode-copilot-release/issues/2433 --- .../contrib/inlineChat/browser/inlineChatController.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index e3ac8629904..9b4f17b4173 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as aria from '../../../../base/browser/ui/aria/aria.js'; -import { Barrier, DeferredPromise, Queue } from '../../../../base/common/async.js'; +import { Barrier, DeferredPromise, Queue, raceCancellation } from '../../../../base/common/async.js'; import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { toErrorMessage } from '../../../../base/common/errorMessage.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; @@ -1187,7 +1187,9 @@ export class InlineChatController implements IEditorContribution { this.cancelSession(); } - await run; + const dispo = token.onCancellationRequested(() => this.cancelSession()); + await raceCancellation(run, token); + dispo.dispose(); return true; } } From f143959b7d5d9bb4c15c33a10a11601db92d5626 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 13 Dec 2024 15:56:41 +0100 Subject: [PATCH 201/479] Sticky Scroll: Adding height and emitter for height change event (#235953) * adding height and emitter for hieght change event * adding the event to the interface * Set the height always through `_setHeight` * Switch to non-overflow mode when it would go on top of sticky scroll --------- Co-authored-by: Alexandru Dima --- .../view/inlineEdits/sideBySideDiff.ts | 9 +++-- .../browser/stickyScrollController.ts | 14 ++++++++ .../browser/stickyScrollWidget.ts | 34 ++++++++++++++++--- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index c5ed83bd5cd..281374e872d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -7,7 +7,7 @@ import { IAction } from '../../../../../../base/common/actions.js'; import { Color } from '../../../../../../base/common/color.js'; import { structuralEquals } from '../../../../../../base/common/equals.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IObservable, autorun, constObservable, derived, derivedOpts, observableValue } from '../../../../../../base/common/observable.js'; +import { IObservable, autorun, constObservable, derived, derivedOpts, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; import { MenuId, MenuItemAction } from '../../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; @@ -25,6 +25,7 @@ import { Position } from '../../../../../common/core/position.js'; import { Range } from '../../../../../common/core/range.js'; import { Command } from '../../../../../common/languages.js'; import { ITextModel } from '../../../../../common/model.js'; +import { StickyScrollController } from '../../../../stickyScroll/browser/stickyScrollController.js'; import { CustomizedMenuWorkbenchToolBar } from '../../hintsWidget/inlineCompletionsHintsWidget.js'; import { PathBuilder, StatusBarViewItem, getOffsetForPos, mapOutFalsy, maxContentWidthInRange, n } from './utils.js'; import { InlineEditWithChanges } from './viewAndDiffProducer.js'; @@ -396,13 +397,17 @@ export class InlineEditsSideBySideDiff extends Disposable { }; }); + private _stickyScrollController = StickyScrollController.get(this._editorObs.editor); + private readonly _stickyScrollHeight = this._stickyScrollController ? observableFromEvent(this._stickyScrollController.onDidChangeStickyScrollHeight, () => this._stickyScrollController!.stickyScrollWidgetHeight) : constObservable(0); + private readonly _shouldOverflow = derived(reader => { const range = this._edit.read(reader)?.originalLineRange; if (!range) { return false; } + const stickyScrollHeight = this._stickyScrollHeight.read(reader); const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); - return top > 0; + return top > stickyScrollHeight; }); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index b5074c87786..01d61e7d614 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -29,10 +29,12 @@ import { StickyRange } from './stickyScrollElement.js'; import { IMouseEvent, StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { FoldingController } from '../../folding/browser/folding.js'; import { FoldingModel, toggleCollapseState } from '../../folding/browser/foldingModel.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; export interface IStickyScrollController { get stickyScrollCandidateProvider(): IStickyLineCandidateProvider; get stickyScrollWidgetState(): StickyScrollWidgetState; + readonly stickyScrollWidgetHeight: number; isFocused(): boolean; focus(): void; focusNext(): void; @@ -41,6 +43,7 @@ export interface IStickyScrollController { findScrollWidgetState(): StickyScrollWidgetState; dispose(): void; selectEditor(): void; + onDidChangeStickyScrollHeight: Event<{ height: number }>; } export class StickyScrollController extends Disposable implements IEditorContribution, IStickyScrollController { @@ -71,6 +74,9 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _showEndForLine: number | undefined; private _minRebuildFromLine: number | undefined; + private readonly _onDidChangeStickyScrollHeight = this._register(new Emitter<{ height: number }>()); + public readonly onDidChangeStickyScrollHeight = this._onDidChangeStickyScrollHeight.event; + constructor( private readonly _editor: ICodeEditor, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @@ -118,6 +124,9 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._register(dom.addDisposableListener(stickyScrollDomNode, dom.EventType.MOUSE_DOWN, (e) => { this._onMouseDown = true; })); + this._register(this._stickyScrollWidget.onDidChangeStickyScrollHeight((e) => { + this._onDidChangeStickyScrollHeight.fire(e); + })); this._onDidResize(); this._readConfiguration(); } @@ -130,6 +139,10 @@ export class StickyScrollController extends Disposable implements IEditorContrib return this._widgetState; } + get stickyScrollWidgetHeight(): number { + return this._stickyScrollWidget.height; + } + public static get(editor: ICodeEditor): IStickyScrollController | null { return editor.getContribution(StickyScrollController.ID); } @@ -413,6 +426,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib const options = this._editor.getOption(EditorOption.stickyScroll); if (options.enabled === false) { this._editor.removeOverlayWidget(this._stickyScrollWidget); + this._resetState(); this._sessionStore.clear(); this._enabled = false; return; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 3d1d583cb28..f805b7879b1 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -19,6 +19,7 @@ import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js'; import { CharacterMapping, RenderLineInput, renderViewLine } from '../../../common/viewLayout/viewLineRenderer.js'; import { foldingCollapsedIcon, foldingExpandedIcon } from '../../folding/browser/foldingDecorations.js'; import { FoldingModel } from '../../folding/browser/foldingModel.js'; +import { Emitter } from '../../../../base/common/event.js'; export class StickyScrollWidgetState { constructor( @@ -62,6 +63,12 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _lastLineRelativePosition: number = 0; private _minContentWidthInPx: number = 0; private _isOnGlyphMargin: boolean = false; + private _height: number = -1; + + public get height(): number { return this._height; } + + private readonly _onDidChangeStickyScrollHeight = this._register(new Emitter<{ height: number }>()); + public readonly onDidChangeStickyScrollHeight = this._onDidChangeStickyScrollHeight.event; constructor( private readonly _editor: ICodeEditor @@ -81,6 +88,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.classList.toggle('peek', _editor instanceof EmbeddedCodeEditorWidget); this._rootDomNode.appendChild(this._lineNumbersDomNode); this._rootDomNode.appendChild(this._linesDomNodeScrollable); + this._setHeight(0); const updateScrollLeftPosition = () => { this._linesDomNode.style.left = this._editor.getOption(EditorOption.stickyScroll).scrollWithEditor ? `-${this._editor.getScrollLeft()}px` : '0px'; @@ -193,7 +201,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } // Keep the lines that need to be updated this._renderedStickyLines = this._renderedStickyLines.slice(0, clearFromLine); - this._rootDomNode.style.display = 'none'; } private _useFoldingOpacityTransition(requireTransitions: boolean) { @@ -213,6 +220,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private async _renderRootNode(state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | undefined, rebuildFromLine: number): Promise { this._clearStickyLinesFromLine(rebuildFromLine); if (!state) { + // make sure the dom is 0 height and display:none + this._setHeight(0); return; } // For existing sticky lines update the top and z-index @@ -237,16 +246,31 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } const widgetHeight = this._lineNumbers.length * this._lineHeight + this._lastLineRelativePosition; - this._rootDomNode.style.display = 'block'; - this._lineNumbersDomNode.style.height = `${widgetHeight}px`; - this._linesDomNodeScrollable.style.height = `${widgetHeight}px`; - this._rootDomNode.style.height = `${widgetHeight}px`; + this._setHeight(widgetHeight); this._rootDomNode.style.marginLeft = '0px'; this._minContentWidthInPx = Math.max(...this._renderedStickyLines.map(l => l.scrollWidth)) + layoutInfo.verticalScrollbarWidth; this._editor.layoutOverlayWidget(this); } + private _setHeight(height: number): void { + if (this._height === height) { + return; + } + this._height = height; + + if (this._height === 0) { + this._rootDomNode.style.display = 'none'; + } else { + this._rootDomNode.style.display = 'block'; + this._lineNumbersDomNode.style.height = `${this._height}px`; + this._linesDomNodeScrollable.style.height = `${this._height}px`; + this._rootDomNode.style.height = `${this._height}px`; + } + + this._onDidChangeStickyScrollHeight.fire({ height: this._height }); + } + private _setFoldingHoverListeners(): void { const showFoldingControls: 'mouseover' | 'always' | 'never' = this._editor.getOption(EditorOption.showFoldingControls); if (showFoldingControls !== 'mouseover') { From e4cf87c882c63ed68e007c798af740e4b1617094 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:06:13 +0100 Subject: [PATCH 202/479] Git - stash untracked filed during checkout (#214768) (#236057) --- extensions/git/src/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index f93340d928e..64905cd55f3 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2619,7 +2619,7 @@ export class CommandCenter { await this.cleanAll(repository); await item.run(repository, opts); } else if (choice === stash || choice === migrate) { - if (await this._stash(repository)) { + if (await this._stash(repository, true)) { await item.run(repository, opts); if (choice === migrate) { From 8c3188b22254e18acebe77acebeb25d538cda7af Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 13 Dec 2024 16:41:56 +0100 Subject: [PATCH 203/479] Fix comments error telemetry (#236060) Fixes #230642 --- .../contrib/comments/browser/commentsController.ts | 10 ++++++++-- .../comments/browser/commentsEditorContribution.ts | 7 +------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 6fc73d49776..88a789ba264 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -47,6 +47,7 @@ import { IAccessibilityService } from '../../../../platform/accessibility/common import { URI } from '../../../../base/common/uri.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { threadHasMeaningfulComments } from './commentsModel.js'; +import { INotificationService } from '../../../../platform/notification/common/notification.js'; export const ID = 'editor.contrib.review'; @@ -485,7 +486,8 @@ export class CommentController implements IEditorContribution { @IContextKeyService contextKeyService: IContextKeyService, @IEditorService private readonly editorService: IEditorService, @IKeybindingService private readonly keybindingService: IKeybindingService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService + @IAccessibilityService private readonly accessibilityService: IAccessibilityService, + @INotificationService private readonly notificationService: INotificationService ) { this._commentInfos = []; this._commentWidgets = []; @@ -1168,7 +1170,11 @@ export class CommentController implements IEditorContribution { if (!newCommentInfos.length || !this.editor?.hasModel()) { this._addInProgress = false; if (!newCommentInfos.length) { - throw new Error(`There are no commenting ranges at the current position (${range ? 'with range' : 'without range'}).`); + if (range) { + this.notificationService.error(nls.localize('comments.addCommand.error', "The cursor must be within a commenting range to add a comment.")); + } else { + this.notificationService.error(nls.localize('comments.addFileCommentCommand.error', "File comments are not allowed on this file.")); + } } return Promise.resolve(); } diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index ce0458cd942..e0441ab93fb 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -280,12 +280,7 @@ registerAction2(class extends Action2 { const position = args?.range ? new Range(args.range.startLineNumber, args.range.startLineNumber, args.range.endLineNumber, args.range.endColumn) : (args?.fileComment ? undefined : activeEditor.getSelection()); - const notificationService = accessor.get(INotificationService); - try { - await controller.addOrToggleCommentAtLine(position, undefined); - } catch (e) { - notificationService.error(nls.localize('comments.addCommand.error', "The cursor must be within a commenting range to add a comment")); - } + await controller.addOrToggleCommentAtLine(position, undefined); } }); From 7ed772b8b0ad0e8e1f7d3294fc9b468d9d1e048c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 13 Dec 2024 16:50:04 +0100 Subject: [PATCH 204/479] remove retry at publish time (#236070) fixes #236067 --- build/azure-pipelines/common/publish.js | 2 +- build/azure-pipelines/common/publish.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js index 742db9d9e97..bcebd076c28 100644 --- a/build/azure-pipelines/common/publish.js +++ b/build/azure-pipelines/common/publish.js @@ -540,7 +540,7 @@ async function processArtifact(artifact, filePath) { const stagingContainerClient = blobServiceClient.getContainerClient('staging'); await stagingContainerClient.createIfNotExists(); const releaseService = await ESRPReleaseService.create(log, e('RELEASE_TENANT_ID'), e('RELEASE_CLIENT_ID'), e('RELEASE_AUTH_CERT'), e('RELEASE_REQUEST_SIGNING_CERT'), stagingContainerClient); - await (0, retry_1.retry)(() => releaseService.createRelease(version, filePath, friendlyFileName)); + await releaseService.createRelease(version, filePath, friendlyFileName); } const { product, os, arch, unprocessedType } = match.groups; const isLegacy = artifact.name.includes('_legacy'); diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index 0987502432b..b8b99c3855b 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -880,7 +880,7 @@ async function processArtifact( stagingContainerClient ); - await retry(() => releaseService.createRelease(version, filePath, friendlyFileName)); + await releaseService.createRelease(version, filePath, friendlyFileName); } const { product, os, arch, unprocessedType } = match.groups!; From 450b0699586e3efa3d731dffe68747fefe138dc3 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 13 Dec 2024 16:50:30 +0100 Subject: [PATCH 205/479] Comments pane shows H1 titles as H1 instead of regular text (#236069) Fixes #236068 --- .../workbench/contrib/comments/browser/commentsTreeViewer.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 69684b02b2d..37c11e7e11e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -250,6 +250,11 @@ export class CommentNodeRenderer implements IListRenderer textDescription.textContent = image.alt ? nls.localize('imageWithLabel', "Image: {0}", image.alt) : nls.localize('image', "Image"); image.parentNode!.replaceChild(textDescription, image); } + const headings = [...renderedComment.element.getElementsByTagName('h1'), ...renderedComment.element.getElementsByTagName('h2'), ...renderedComment.element.getElementsByTagName('h3'), ...renderedComment.element.getElementsByTagName('h4'), ...renderedComment.element.getElementsByTagName('h5'), ...renderedComment.element.getElementsByTagName('h6')]; + for (const heading of headings) { + const textNode = document.createTextNode(heading.textContent || ''); + heading.parentNode!.replaceChild(textNode, heading); + } while ((renderedComment.element.children.length > 1) && (renderedComment.element.firstElementChild?.tagName === 'HR')) { renderedComment.element.removeChild(renderedComment.element.firstElementChild); } From 974044db9bffb69fb4ac5c301c847a38d2779ab1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 13 Dec 2024 16:58:21 +0100 Subject: [PATCH 206/479] Use proper delimiter in tasks paths (#236047) --- src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 045a9e63b44..e44eaa47cf9 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -619,7 +619,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { private async _resolveAndFindExecutable(systemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, cwd: string | undefined, envPath: string | undefined): Promise { const command = await this._configurationResolverService.resolveAsync(workspaceFolder, CommandString.value(task.command.name!)); cwd = cwd ? await this._configurationResolverService.resolveAsync(workspaceFolder, cwd) : undefined; - const paths = envPath ? await Promise.all(envPath.split(path.delimiter).map(p => this._configurationResolverService.resolveAsync(workspaceFolder, p))) : undefined; + const delimiter = (await this._pathService.path).delimiter; + const paths = envPath ? await Promise.all(envPath.split(delimiter).map(p => this._configurationResolverService.resolveAsync(workspaceFolder, p))) : undefined; const foundExecutable = await systemInfo?.findExecutable(command, cwd, paths); if (foundExecutable) { return foundExecutable; From 4b254c2da56e2b3a2faf41d3136c188350e00d44 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:21:25 +0100 Subject: [PATCH 207/479] Git - auto stash should restore the stash when the operation fails (#232929) (#236059) --- extensions/git/src/repository.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index e3b924d5868..73a54cef4af 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -2465,10 +2465,12 @@ export class Repository implements Disposable { } await this.repository.createStash(undefined, true); - const result = await runOperation(); - await this.repository.popStash(); - - return result; + try { + const result = await runOperation(); + return result; + } finally { + await this.repository.popStash(); + } } private onFileChange(_uri: Uri): void { From d70dd875a9c01613a52a5e83dab807186cf67001 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 13 Dec 2024 17:25:51 +0100 Subject: [PATCH 208/479] Make sure the background fill for the new code renders on top of other editor widgets and remove DOM warning (#236073) --- .../view/inlineEdits/sideBySideDiff.ts | 26 ++++++++++++------- .../browser/view/inlineEdits/utils.ts | 12 +++++++-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 281374e872d..331eb28bc7b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -432,14 +432,6 @@ export class InlineEditsSideBySideDiff extends Disposable { transform: 'translate(-0.5 -0.5)', style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, }, [ - n.svgElem('path', { - class: 'extendedModifiedBackgroundCoverUp', - d: this._extendedModifiedPath, - style: { - fill: 'var(--vscode-editor-background, transparent)', - strokeWidth: '1px', - } - }), n.svgElem('path', { class: 'rightOfModifiedBackgroundCoverUp', d: this._previewEditorLayoutInfo.map(layoutInfo => layoutInfo && new PathBuilder() @@ -455,6 +447,20 @@ export class InlineEditsSideBySideDiff extends Disposable { }), ]).keepUpdated(this._store); + private readonly _foregroundBackgroundSvg = n.svg({ + transform: 'translate(-0.5 -0.5)', + style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, + }, [ + n.svgElem('path', { + class: 'extendedModifiedBackgroundCoverUp', + d: this._extendedModifiedPath, + style: { + fill: 'var(--vscode-editor-background, transparent)', + strokeWidth: '1px', + } + }), + ]).keepUpdated(this._store); + private readonly _foregroundSvg = n.svg({ transform: 'translate(-0.5 -0.5)', style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, @@ -544,7 +550,7 @@ export class InlineEditsSideBySideDiff extends Disposable { }, }, [ this._backgroundSvg, - derived(this, reader => this._shouldOverflow.read(reader) ? [] : [this._editorContainer, this._foregroundSvg]), + derived(this, reader => this._shouldOverflow.read(reader) ? [] : [this._foregroundBackgroundSvg, this._editorContainer, this._foregroundSvg]), ]).keepUpdated(this._store); private readonly _overflowView = n.div({ @@ -558,6 +564,6 @@ export class InlineEditsSideBySideDiff extends Disposable { display: this._display, }, }, [ - derived(this, reader => this._shouldOverflow.read(reader) ? [this._editorContainer, this._foregroundSvg] : []), + derived(this, reader => this._shouldOverflow.read(reader) ? [this._foregroundBackgroundSvg, this._editorContainer, this._foregroundSvg] : []), ]).keepUpdated(this._store); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index a97c9fe15f5..16971c2ebc6 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -325,10 +325,10 @@ export abstract class ObserverNode extends Disposab } else { if (isObservable(value)) { this._deriveds.push(derived(this, reader => { - this._element.setAttribute(camelCaseToHyphenCase(key), value.read(reader) as any); + setOrRemoveAttribute(this._element, key, value.read(reader)); })); } else { - this._element.setAttribute(camelCaseToHyphenCase(key), value.toString()); + setOrRemoveAttribute(this._element, key, value); } } } @@ -393,6 +393,14 @@ export class ObserverNodeWithElement extends Observ } } +function setOrRemoveAttribute(element: Element, key: string, value: unknown) { + if (value === null || value === undefined) { + element.removeAttribute(camelCaseToHyphenCase(key)); + } else { + element.setAttribute(camelCaseToHyphenCase(key), String(value)); + } +} + function camelCaseToHyphenCase(str: string) { return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); } From e7b43262d29ff638df67c85eedc472a6c7685999 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 13 Dec 2024 17:39:05 +0100 Subject: [PATCH 209/479] Improve layouting of side-by-side (#236075) --- .../browser/view/inlineEdits/sideBySideDiff.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 331eb28bc7b..cf7c1f94cf4 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -2,6 +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 { getWindow } from '../../../../../../base/browser/dom.js'; import { ActionViewItem } from '../../../../../../base/browser/ui/actionbar/actionViewItems.js'; import { IAction } from '../../../../../../base/common/actions.js'; import { Color } from '../../../../../../base/common/color.js'; @@ -340,14 +341,23 @@ export class InlineEditsSideBySideDiff extends Disposable { const editorContentMaxWidthInRange = maxContentWidthInRange(this._editorObs, state.originalDisplayRange, reader); const editorLayout = this._editorObs.layoutInfo.read(reader); const previewWidth = this._previewEditorWidth.read(reader); - const editorContentAreaWidth = editorLayout.width - editorLayout.contentLeft - editorLayout.minimap.minimapWidth - editorLayout.verticalScrollbarWidth; + const editorContentAreaWidth = editorLayout.contentWidth - editorLayout.verticalScrollbarWidth; + const clientContentAreaRight = editorLayout.contentLeft + editorLayout.contentWidth + this._editor.getContainerDomNode().getBoundingClientRect().left; + const remainingWidthRightOfContent = getWindow(this._editor.getContainerDomNode()).outerWidth - clientContentAreaRight; + const desiredMinimumWidth = Math.min(editorLayout.contentWidth * 0.3, previewWidth, 100); + const IN_EDITOR_DISPLACEMENT = 0; + const maximumAvailableWidth = IN_EDITOR_DISPLACEMENT + remainingWidthRightOfContent; const cursorPos = this._cursorPosIfTouchesEdit.read(reader); const maxPreviewEditorLeft = Math.max( - editorContentAreaWidth * 0.65 + horizontalScrollOffset - 10, - editorContentAreaWidth - previewWidth - 70 + horizontalScrollOffset - 10, - cursorPos ? getOffsetForPos(this._editorObs, cursorPos, reader) + 50 : 0 + // We're starting from the content area right and moving it left by IN_EDITOR_DISPLACEMENT and also by an ammount to ensure some mimum desired width + editorContentAreaWidth + horizontalScrollOffset - IN_EDITOR_DISPLACEMENT - Math.max(0, desiredMinimumWidth - maximumAvailableWidth), + // But we don't want that the moving left ends up covering the cursor, so this will push it to the right again + Math.min( + cursorPos ? getOffsetForPos(this._editorObs, cursorPos, reader) + 50 : 0, + editorContentAreaWidth + horizontalScrollOffset + ) ); const previewEditorLeftInTextArea = Math.min(editorContentMaxWidthInRange + 20, maxPreviewEditorLeft); From e044a71719609b9701aa0d577523d18f92efc32c Mon Sep 17 00:00:00 2001 From: aslezar <97354675+aslezar@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:22:13 +0530 Subject: [PATCH 210/479] Fix incorrect GLIBC version parsing Fixes #230370 --- cli/src/util/prereqs.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs index e0fba272fcf..dc469fd30cd 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -15,7 +15,7 @@ use super::errors::CodeError; lazy_static! { static ref LDCONFIG_STDC_RE: Regex = Regex::new(r"libstdc\+\+.* => (.+)").unwrap(); - static ref LDD_VERSION_RE: BinRegex = BinRegex::new(r"^ldd.*(.+)\.(.+)\s").unwrap(); + static ref LDD_VERSION_RE: BinRegex = BinRegex::new(r"^ldd.*\s(\d+)\.(\d+)(?:\.(\d+))?\s").unwrap(); static ref GENERIC_VERSION_RE: Regex = Regex::new(r"^([0-9]+)\.([0-9]+)$").unwrap(); static ref LIBSTD_CXX_VERSION_RE: BinRegex = BinRegex::new(r"GLIBCXX_([0-9]+)\.([0-9]+)(?:\.([0-9]+))?").unwrap(); @@ -271,10 +271,10 @@ fn check_for_sufficient_glibcxx_versions(contents: Vec) -> Result Option { LDD_VERSION_RE.captures(output).map(|m| SimpleSemver { - major: m.get(1).map_or(0, |s| u32_from_bytes(s.as_bytes())), - minor: m.get(2).map_or(0, |s| u32_from_bytes(s.as_bytes())), - patch: 0, - }) + major: m.get(1).map_or(0, |s| u32_from_bytes(s.as_bytes())), + minor: m.get(2).map_or(0, |s| u32_from_bytes(s.as_bytes())), + patch: 0, + }) } #[allow(dead_code)] @@ -401,5 +401,18 @@ mod tests { extract_ldd_version(&actual), Some(SimpleSemver::new(2, 31, 0)), ); + + let actual2 = "ldd (GNU libc) 2.40.9000 + Copyright (C) 2024 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Written by Roland McGrath and Ulrich Drepper." + .to_owned() + .into_bytes(); + assert_eq!( + extract_ldd_version(&actual2), + Some(SimpleSemver::new(2, 40, 0)), + ); } + } From 2468418bd9f6e3a5b7d363c3fee274d5e426f64b Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 13 Dec 2024 10:57:53 -0600 Subject: [PATCH 211/479] pass in prefix vs value to completion extension providers so command line excludes ghost text (#236000) fixes #235996 --- .../terminalContrib/suggest/browser/terminalSuggestAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index 4594a1b60a8..83c87e8cec2 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -156,7 +156,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest await this._extensionService.activateByEvent('onTerminalCompletionsRequested'); } - const providedCompletions = await this._terminalCompletionService.provideCompletions(this._promptInputModel.value, this._promptInputModel.cursorIndex, this._shellType, token, triggerCharacter, doNotRequestExtensionCompletions); + const providedCompletions = await this._terminalCompletionService.provideCompletions(this._promptInputModel.prefix, this._promptInputModel.cursorIndex, this._shellType, token, triggerCharacter, doNotRequestExtensionCompletions); if (!providedCompletions?.length || token.isCancellationRequested) { return; } From 1a56b0b5a72c8f8cd5cab62c801b4555b9b4c2b1 Mon Sep 17 00:00:00 2001 From: LemmusLemmus <63229554+LemmusLemmus@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:58:57 +0100 Subject: [PATCH 212/479] Add $ to surrounding pairs in markdown (#233981) The dollar sign is used for inline mathematical expressions. --- extensions/markdown-basics/language-configuration.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/markdown-basics/language-configuration.json b/extensions/markdown-basics/language-configuration.json index 6e1766db02c..935b1c66250 100644 --- a/extensions/markdown-basics/language-configuration.json +++ b/extensions/markdown-basics/language-configuration.json @@ -83,6 +83,10 @@ [ "~", "~" + ], + [ + "$", + "$" ] ], "folding": { From 5ae6f1e82930572167dd27a86570e49f9ae658f0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 13 Dec 2024 18:04:38 +0100 Subject: [PATCH 213/479] chat overlay fixes (#236079) * fixes disabled button color in chat overlay * fixes https://github.com/microsoft/vscode-copilot/issues/10930 * fix reveal after streaming issues --- .../chat/browser/chatEditorController.ts | 106 ++++++++++-------- .../contrib/chat/browser/chatEditorOverlay.ts | 31 +++-- .../chat/browser/media/chatEditorOverlay.css | 12 +- 3 files changed, 78 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts index 0cca055ece2..18a7c1a258e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts @@ -6,7 +6,7 @@ import './media/chatEditorController.css'; import { addStandardDisposableListener, getTotalWidth } from '../../../../base/browser/dom.js'; import { Disposable, DisposableStore, dispose, toDisposable } from '../../../../base/common/lifecycle.js'; -import { autorun, autorunWithStore, derived, IObservable, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; +import { autorun, autorunWithStore, derived, IObservable, observableFromEvent, observableValue, transaction } from '../../../../base/common/observable.js'; import { themeColorFromId } from '../../../../base/common/themables.js'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IOverlayWidgetPositionCoordinates, IViewZone, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; import { LineSource, renderLines, RenderOptions } from '../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; @@ -55,8 +55,11 @@ export class ChatEditorController extends Disposable implements IEditorContribut return controller; } - private readonly _currentChange = observableValue(this, undefined); - readonly currentChange: IObservable = this._currentChange; + private readonly _currentEntryIndex = observableValue(this, undefined); + readonly currentEntryIndex: IObservable = this._currentEntryIndex; + + private readonly _currentChangeIndex = observableValue(this, undefined); + readonly currentChangeIndex: IObservable = this._currentChangeIndex; private _scrollLock: boolean = false; @@ -76,20 +79,28 @@ export class ChatEditorController extends Disposable implements IEditorContribut const lineHeightObs = observableCodeEditor(this._editor).getOption(EditorOption.lineHeight); const modelObs = observableCodeEditor(this._editor).model; - // scroll along unless "another" scroll happens - let ignoreScrollEvent = false; - this._store.add(this._editor.onDidScrollChange(e => { - if (e.scrollTopChanged && !ignoreScrollEvent) { - // this._scrollLock = true; - } - })); this._store.add(autorun(r => { const session = this._chatEditingService.currentEditingSessionObs.read(r); this._ctxRequestInProgress.set(session?.state.read(r) === ChatEditingSessionState.StreamingEdits); })); - let didReveal = false; + + const entryForEditor = derived(r => { + const model = modelObs.read(r); + const session = this._chatEditingService.currentEditingSessionObs.read(r); + if (!session) { + return undefined; + } + const entry = model?.uri ? session.readEntry(model.uri, r) : undefined; + if (!entry || entry.state.read(r) !== WorkingSetEntryState.Modified) { + return undefined; + } + return { session, entry }; + }); + + + let didReval = false; this._register(autorunWithStore((r, store) => { @@ -98,55 +109,48 @@ export class ChatEditorController extends Disposable implements IEditorContribut return; } - fontInfoObs.read(r); - lineHeightObs.read(r); - - const model = modelObs.read(r); + const currentEditorEntry = entryForEditor.read(r); - const session = this._chatEditingService.currentEditingSessionObs.read(r); - const entry = model?.uri ? session?.readEntry(model.uri, r) : undefined; - - if (!entry || entry.state.read(r) !== WorkingSetEntryState.Modified) { + if (!currentEditorEntry) { this._clearRendering(); - didReveal = false; + didReval = false; return; } + const { session, entry } = currentEditorEntry; + + const entryIndex = session.entries.read(r).indexOf(entry); + this._currentEntryIndex.set(entryIndex, undefined); + if (entry.isCurrentlyBeingModified.read(r)) { + // while modified: scroll along unless locked + if (!this._scrollLock) { + const maxLineNumber = entry.maxLineNumber.read(r); + this._editor.revealLineNearTop(maxLineNumber, ScrollType.Smooth); + } const domNode = this._editor.getDomNode(); if (domNode) { store.add(addStandardDisposableListener(domNode, 'wheel', () => { this._scrollLock = true; })); } - } + } else { + // done: render diff + fontInfoObs.read(r); + lineHeightObs.read(r); - try { - ignoreScrollEvent = true; - if (!this._scrollLock && !didReveal) { - const maxLineNumber = entry.maxLineNumber.read(r); - this._editor.revealLineNearTop(maxLineNumber, ScrollType.Smooth); - } const diff = entry?.diffInfo.read(r); this._updateWithDiff(entry, diff); - if (!entry.isCurrentlyBeingModified.read(r)) { - this.initNavigation(); - - if (!this._scrollLock && !didReveal) { - const currentPosition = this._currentChange.read(r); - if (currentPosition) { - this._editor.revealLine(currentPosition.lineNumber, ScrollType.Smooth); - didReveal = true; - } else { - didReveal = this.revealNext(); - } - } + + if (!didReval && !diff.identical) { + didReval = true; + this.revealNext(); } - } finally { - ignoreScrollEvent = false; } })); + // ---- readonly while streaming + const shouldBeReadOnly = derived(this, r => { const value = this._chatEditingService.currentEditingSessionObs.read(r); if (!value || value.state.read(r) !== ChatEditingSessionState.StreamingEdits) { @@ -200,7 +204,10 @@ export class ChatEditorController extends Disposable implements IEditorContribut this._diffVisualDecorations.clear(); this._diffLineDecorations.clear(); this._ctxHasEditorModification.reset(); - this._currentChange.set(undefined, undefined); + transaction(tx => { + this._currentEntryIndex.set(undefined, tx); + this._currentChangeIndex.set(undefined, tx); + }); this._scrollLock = false; } @@ -423,10 +430,8 @@ export class ChatEditorController extends Disposable implements IEditorContribut initNavigation(): void { const position = this._editor.getPosition(); - const range = position && this._diffLineDecorations.getRanges().find(r => r.containsPosition(position)); - if (range) { - this._currentChange.set(position, undefined); - } + const target = position ? this._diffLineDecorations.getRanges().findIndex(r => r.containsPosition(position)) : -1; + this._currentChangeIndex.set(target >= 0 ? target : undefined, undefined); } revealNext(strict = false): boolean { @@ -440,6 +445,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut private _reveal(next: boolean, strict: boolean): boolean { const position = this._editor.getPosition(); if (!position) { + this._currentChangeIndex.set(undefined, undefined); return false; } @@ -448,6 +454,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut .sort((a, b) => Range.compareRangesUsingStarts(a, b)); if (decorations.length === 0) { + this._currentChangeIndex.set(undefined, undefined); return false; } @@ -464,18 +471,19 @@ export class ChatEditorController extends Disposable implements IEditorContribut } if (strict && (target < 0 || target >= decorations.length)) { + this._currentChangeIndex.set(undefined, undefined); return false; } target = (target + decorations.length) % decorations.length; - const targetPosition = next ? decorations[target].getStartPosition() : decorations[target].getEndPosition(); - - this._currentChange.set(targetPosition, undefined); + this._currentChangeIndex.set(target, undefined); + const targetPosition = next ? decorations[target].getStartPosition() : decorations[target].getEndPosition(); this._editor.setPosition(targetPosition); this._editor.revealPositionInCenter(targetPosition, ScrollType.Smooth); this._editor.focus(); + return true; } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts index 6a09f5ef275..bb94d4e0fc9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts @@ -203,25 +203,24 @@ class ChatEditorOverlayWidget implements IOverlayWidget { this._showStore.add(autorun(r => { - const position = ChatEditorController.get(this._editor)?.currentChange.read(r); + const ctrl = ChatEditorController.get(this._editor); + + const entryIndex = ctrl?.currentEntryIndex.read(r); + const changeIndex = ctrl?.currentChangeIndex.read(r); + const entries = session.entries.read(r); + let activeIdx = entryIndex !== undefined && changeIndex !== undefined + ? changeIndex + : -1; + let changes = 0; - let activeIdx = -1; - for (const entry of entries) { - const diffInfo = entry.diffInfo.read(r); - - if (activeIdx !== -1 || entry !== activeEntry) { - // just add up - changes += diffInfo.changes.length; - - } else { - for (const change of diffInfo.changes) { - if (position && change.modified.includes(position.lineNumber)) { - activeIdx = changes; - } - changes += 1; - } + for (let i = 0; i < entries.length; i++) { + const diffInfo = entries[i].diffInfo.read(r); + changes += diffInfo.changes.length; + + if (entryIndex !== undefined && i < entryIndex) { + activeIdx += diffInfo.changes.length; } } diff --git a/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css b/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css index 67dfaedc4b9..26ece80c64c 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css @@ -53,10 +53,10 @@ color: var(--vscode-button-foreground); } -.chat-editor-overlay-widget .action-item.disabled > .action-label.codicon::before, -.chat-editor-overlay-widget .action-item.disabled > .action-label.codicon, -.chat-editor-overlay-widget .action-item.disabled > .action-label, -.chat-editor-overlay-widget .action-item.disabled > .action-label:hover { +.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label.codicon::before, +.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label.codicon, +.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label, +.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label:hover { color: var(--vscode-button-foreground); opacity: 0.7; } @@ -66,8 +66,8 @@ font-variant-numeric: tabular-nums; } -.chat-editor-overlay-widget .action-item.label-item > .action-label, -.chat-editor-overlay-widget .action-item.label-item > .action-label:hover { +.chat-editor-overlay-widget .monaco-action-bar .action-item.label-item > .action-label, +.chat-editor-overlay-widget .monaco-action-bar .action-item.label-item > .action-label:hover { color: var(--vscode-button-foreground); opacity: 1; } From e7f022a392ed96a234ccca4106796e8a7b2c685a Mon Sep 17 00:00:00 2001 From: oltolm Date: Fri, 13 Dec 2024 18:10:50 +0100 Subject: [PATCH 214/479] debug: ignore error when stopping a process (#236009) ERR Invalid debug adapter: Error: Invalid debug adapter --- src/vs/workbench/api/browser/mainThreadDebugService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index cccdce7eb4c..69edcaa8ecd 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -397,7 +397,8 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } public $acceptDAExit(handle: number, code: number, signal: string) { - this.getDebugAdapter(handle).fireExit(handle, code, signal); + // don't use getDebugAdapter since an error can be expected on a post-close + this._debugAdapters.get(handle)?.fireExit(handle, code, signal); } private getDebugAdapter(handle: number): ExtensionHostDebugAdapter { From b911e7c4f0160a46efba471d2f96cbd3ae1698fd Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 13 Dec 2024 18:14:42 +0100 Subject: [PATCH 215/479] icon themes: unify validation of font properties (#236081) * allow emojiis in icon fonts * icon themes: unify validation of font properties --- src/vs/platform/theme/common/iconRegistry.ts | 18 +++++++++++--- .../themes/browser/fileIconThemeData.ts | 2 +- .../themes/browser/productIconThemeData.ts | 5 ++-- .../themes/common/fileIconThemeSchema.ts | 24 ++++++++++--------- .../themes/common/iconExtensionPoint.ts | 10 ++++---- .../themes/common/productIconThemeSchema.ts | 18 ++++---------- 6 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/vs/platform/theme/common/iconRegistry.ts b/src/vs/platform/theme/common/iconRegistry.ts index 5dee6ce44d6..775c1cf1d4a 100644 --- a/src/vs/platform/theme/common/iconRegistry.ts +++ b/src/vs/platform/theme/common/iconRegistry.ts @@ -145,6 +145,19 @@ export interface IIconRegistry { getIconFont(id: string): IconFontDefinition | undefined; } +// regexes for validation of font properties + +export const fontIdRegex = /^([\w_-]+)$/; +export const fontStyleRegex = /^(normal|italic|(oblique[ \w\s-]+))$/; +export const fontWeightRegex = /^(normal|bold|lighter|bolder|(\d{0-1000}))$/; +export const fontSizeRegex = /^([\w_.%+-]+)$/; +export const fontFormatRegex = /^woff|woff2|truetype|opentype|embedded-opentype|svg$/; +export const fontCharacterRegex = /^([^\\]|\\[a-fA-F0-9]+)$/u; +export const fontColorRegex = /^#[0-9a-fA-F]{0,6}$/; + +export const fontCharacterErrorMessage = localize('schema.fontCharacter.formatError', 'The fontCharacter must be a single letter or a backslash followed by unicode code points in hexadecimal.'); +export const fontIdErrorMessage = localize('schema.fontId.formatError', 'The font ID must only contain letters, numbers, underscores and dashes.'); + class IconRegistry implements IIconRegistry { private readonly _onDidChange = new Emitter(); @@ -156,8 +169,8 @@ class IconRegistry implements IIconRegistry { icons: { type: 'object', properties: { - fontId: { type: 'string', description: localize('iconDefinition.fontId', 'The id of the font to use. If not set, the font that is defined first is used.') }, - fontCharacter: { type: 'string', description: localize('iconDefinition.fontCharacter', 'The font character associated with the icon definition.') } + fontId: { type: 'string', description: localize('iconDefinition.fontId', 'The id of the font to use. If not set, the font that is defined first is used.'), pattern: fontIdRegex.source, patternErrorMessage: fontIdErrorMessage }, + fontCharacter: { type: 'string', description: localize('iconDefinition.fontCharacter', 'The font character associated with the icon definition.'), pattern: fontCharacterRegex.source, patternErrorMessage: fontCharacterErrorMessage } }, additionalProperties: false, defaultSnippets: [{ body: { fontCharacter: '\\\\e030' } }] @@ -318,7 +331,6 @@ iconRegistry.onDidChange(() => { } }); - //setTimeout(_ => console.log(iconRegistry.toString()), 5000); diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts index c60faca2144..e1d349be481 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -13,7 +13,7 @@ import { getParseErrorMessage } from '../../../../base/common/jsonErrorMessages. import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IExtensionResourceLoaderService } from '../../../../platform/extensionResourceLoader/common/extensionResourceLoader.js'; import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { fontCharacterRegex, fontColorRegex, fontSizeRegex } from '../common/productIconThemeSchema.js'; +import { fontCharacterRegex, fontColorRegex, fontSizeRegex } from '../../../../platform/theme/common/iconRegistry.js'; import * as css from '../../../../base/browser/cssValue.js'; import { fileIconSelectorEscape } from '../../../../editor/common/services/getIconClasses.js'; diff --git a/src/vs/workbench/services/themes/browser/productIconThemeData.ts b/src/vs/workbench/services/themes/browser/productIconThemeData.ts index 8961bfe3b2b..1e0a9fa295c 100644 --- a/src/vs/workbench/services/themes/browser/productIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/productIconThemeData.ts @@ -11,10 +11,9 @@ import * as Json from '../../../../base/common/json.js'; import { ExtensionData, IThemeExtensionPoint, IWorkbenchProductIconTheme, ThemeSettingDefaults } from '../common/workbenchThemeService.js'; import { getParseErrorMessage } from '../../../../base/common/jsonErrorMessages.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { fontIdRegex, fontWeightRegex, fontStyleRegex, fontFormatRegex, fontCharacterRegex } from '../common/productIconThemeSchema.js'; import { isObject, isString } from '../../../../base/common/types.js'; import { ILogService } from '../../../../platform/log/common/log.js'; -import { IconDefinition, getIconRegistry, IconContribution, IconFontDefinition, IconFontSource } from '../../../../platform/theme/common/iconRegistry.js'; +import { IconDefinition, getIconRegistry, IconContribution, IconFontDefinition, IconFontSource, fontIdRegex, fontWeightRegex, fontStyleRegex, fontFormatRegex, fontCharacterRegex, fontCharacterErrorMessage } from '../../../../platform/theme/common/iconRegistry.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IExtensionResourceLoaderService } from '../../../../platform/extensionResourceLoader/common/extensionResourceLoader.js'; @@ -256,7 +255,7 @@ function _loadProductIconThemeDocument(fileService: IExtensionResourceLoaderServ warnings.push(nls.localize('error.icon.font', 'Skipping icon definition \'{0}\'. Unknown font.', iconId)); } } else { - warnings.push(nls.localize('error.icon.fontCharacter', 'Skipping icon definition \'{0}\'. Unknown fontCharacter. Must use a sing; character or a \\ followed by a Unicode code points in hexadecimal.', iconId)); + warnings.push(nls.localize('error.icon.fontCharacter', 'Skipping icon definition \'{0}\': {1}', iconId, fontCharacterErrorMessage)); } } return { iconDefinitions }; diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index 685c3375f00..f51191117c5 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -7,7 +7,7 @@ import * as nls from '../../../../nls.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; -import { fontWeightRegex, fontStyleRegex, fontSizeRegex, fontIdRegex, fontCharacterRegex, fontColorRegex } from './productIconThemeSchema.js'; +import { fontWeightRegex, fontStyleRegex, fontSizeRegex, fontIdRegex, fontCharacterRegex, fontColorRegex, fontCharacterErrorMessage, fontIdErrorMessage } from '../../../../platform/theme/common/iconRegistry.js'; const schemaId = 'vscode://schemas/icon-theme'; const schema: IJSONSchema = { @@ -148,8 +148,8 @@ const schema: IJSONSchema = { id: { type: 'string', description: nls.localize('schema.id', 'The ID of the font.'), - pattern: fontIdRegex, - patternErrorMessage: nls.localize('schema.id.formatError', 'The ID must only contain letter, numbers, underscore and minus.') + pattern: fontIdRegex.source, + patternErrorMessage: fontIdErrorMessage }, src: { type: 'array', @@ -176,17 +176,17 @@ const schema: IJSONSchema = { weight: { type: 'string', description: nls.localize('schema.font-weight', 'The weight of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight for valid values.'), - pattern: fontWeightRegex + pattern: fontWeightRegex.source }, style: { type: 'string', description: nls.localize('schema.font-style', 'The style of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-style for valid values.'), - pattern: fontStyleRegex + pattern: fontStyleRegex.source }, size: { type: 'string', description: nls.localize('schema.font-size', 'The default size of the font. We strongly recommend using a percentage value, for example: 125%.'), - pattern: fontSizeRegex + pattern: fontSizeRegex.source } }, required: [ @@ -209,23 +209,25 @@ const schema: IJSONSchema = { fontCharacter: { type: 'string', description: nls.localize('schema.fontCharacter', 'When using a glyph font: The character in the font to use.'), - pattern: fontCharacterRegex, - patternErrorMessage: nls.localize('schema.fontCharacter.formatError', 'The fontCharacter must be a single letter or a backslash and followed by unicode code points in hexadecimal.') + pattern: fontCharacterRegex.source, + patternErrorMessage: fontCharacterErrorMessage }, fontColor: { type: 'string', format: 'color-hex', description: nls.localize('schema.fontColor', 'When using a glyph font: The color to use.'), - pattern: fontColorRegex + pattern: fontColorRegex.source }, fontSize: { type: 'string', description: nls.localize('schema.fontSize', 'When using a font: The font size in percentage to the text font. If not set, defaults to the size in the font definition.'), - pattern: fontSizeRegex + pattern: fontSizeRegex.source }, fontId: { type: 'string', - description: nls.localize('schema.fontId', 'When using a font: The id of the font. If not set, defaults to the first font definition.') + description: nls.localize('schema.fontId', 'When using a font: The id of the font. If not set, defaults to the first font definition.'), + pattern: fontIdRegex.source, + patternErrorMessage: fontIdErrorMessage } } } diff --git a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts index 0a58dbcdbbf..9b554226c3f 100644 --- a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts @@ -5,13 +5,12 @@ import * as nls from '../../../../nls.js'; import { ExtensionsRegistry } from '../../extensions/common/extensionsRegistry.js'; -import { IIconRegistry, Extensions as IconRegistryExtensions } from '../../../../platform/theme/common/iconRegistry.js'; +import { IIconRegistry, Extensions as IconRegistryExtensions, fontCharacterErrorMessage, fontCharacterRegex } from '../../../../platform/theme/common/iconRegistry.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import * as resources from '../../../../base/common/resources.js'; import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js'; import { extname, posix } from '../../../../base/common/path.js'; -import { fontCharacterRegex } from './productIconThemeSchema.js'; interface IIconExtensionPoint { [id: string]: { @@ -55,8 +54,8 @@ const iconConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint Date: Fri, 13 Dec 2024 11:17:26 -0600 Subject: [PATCH 216/479] respond to config change for terminal suggestions (#235999) * fix #235071 * tweak * fixes #235996 * tweak --- .../browser/terminal.suggest.contribution.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts index aac182d3ebb..82b9dc7d514 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts @@ -61,6 +61,24 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo this._pwshAddon?.dispose(); })); this._terminalSuggestWidgetVisibleContextKey = TerminalContextKeys.suggestWidgetVisible.bindTo(this._contextKeyService); + this.add(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(TerminalSuggestSettingId.EnableExtensionCompletions) || e.affectsConfiguration(TerminalSuggestSettingId.Enabled)) { + const extensionCompletionsEnabled = this._configurationService.getValue(terminalSuggestConfigSection).enableExtensionCompletions; + const completionsEnabled = this._configurationService.getValue(terminalSuggestConfigSection).enabled; + if (!extensionCompletionsEnabled || !completionsEnabled) { + this._addon.clear(); + } + if (!completionsEnabled) { + this._pwshAddon.clear(); + } + const xtermRaw = this._ctx.instance.xterm?.raw; + if (!!xtermRaw && extensionCompletionsEnabled || completionsEnabled) { + if (xtermRaw) { + this._loadAddons(xtermRaw); + } + } + } + })); } xtermOpen(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { From 9547abafe7c0da3f0b658ada51ea7967f368e221 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 13 Dec 2024 18:29:22 +0100 Subject: [PATCH 217/479] Rerender sticky scroll on folding settings change (#236083) adding code to rerender sticky scroll on folding settings change --- .../contrib/stickyScroll/browser/stickyScrollController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 01d61e7d614..f4ef1a671a9 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -468,7 +468,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._readConfiguration(); } - if (event.hasChanged(EditorOption.lineNumbers)) { + if (event.hasChanged(EditorOption.lineNumbers) || event.hasChanged(EditorOption.folding) || event.hasChanged(EditorOption.showFoldingControls)) { this._renderStickyScroll(0); } } From 8abb9e03d6c150b5490f4a55588be4930fdb5ab6 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 13 Dec 2024 18:49:57 +0100 Subject: [PATCH 218/479] Adding back border for glyph hover widget (#236088) adding back border for glyph hover widget --- src/vs/editor/contrib/hover/browser/hover.css | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/hover.css b/src/vs/editor/contrib/hover/browser/hover.css index b9da587c918..1314484424b 100644 --- a/src/vs/editor/contrib/hover/browser/hover.css +++ b/src/vs/editor/contrib/hover/browser/hover.css @@ -13,7 +13,14 @@ box-sizing: content-box; } +.monaco-editor .monaco-resizable-hover > .monaco-hover { + border: none; + border-radius: none; +} + .monaco-editor .monaco-hover { + border: 1px solid var(--vscode-editorHoverWidget-border); + border-radius: 3px; color: var(--vscode-editorHoverWidget-foreground); background-color: var(--vscode-editorHoverWidget-background); } @@ -31,7 +38,7 @@ } .monaco-editor .monaco-hover .hover-row .hover-row-contents { - min-width:0; + min-width: 0; display: flex; flex-direction: column; } @@ -65,5 +72,3 @@ .monaco-editor .monaco-hover code { background-color: var(--vscode-textCodeBlock-background); } - - From bd786750d9f1750cdeea7902a222d7ec9e347279 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 13 Dec 2024 18:53:24 +0100 Subject: [PATCH 219/479] Add `useMixedLinesDiff: forStableInsertions` (#236090) --- src/vs/editor/common/config/editorOptions.ts | 8 +- .../browser/model/inlineCompletionsModel.ts | 10 +-- .../view/inlineEdits/inlineDiffView.ts | 6 +- .../browser/view/inlineEdits/view.ts | 78 +++++++++++++++---- .../view/inlineEdits/viewAndDiffProducer.ts | 6 +- src/vs/monaco.d.ts | 2 +- 6 files changed, 79 insertions(+), 31 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 8bd08d2ee1a..0c255963581 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4195,7 +4195,7 @@ export interface IInlineSuggestOptions { edits?: { experimental?: { enabled?: boolean; - useMixedLinesDiff?: 'never' | 'whenPossible' | 'afterJumpWhenPossible'; + useMixedLinesDiff?: 'never' | 'whenPossible' | 'forStableInsertions' | 'afterJumpWhenPossible'; useInterleavedLinesDiff?: 'never' | 'always' | 'afterJump'; onlyShowWhenCloseToCursor?: boolean; }; @@ -4227,7 +4227,7 @@ class InlineEditorSuggest extends BaseEditorOption(this, undefined); - private readonly _primaryPosition = derived(this, reader => this._positions.read(reader)[0] ?? new Position(1, 1)); + public readonly primaryPosition = derived(this, reader => this._positions.read(reader)[0] ?? new Position(1, 1)); private _isAcceptingPartially = false; public get isAcceptingPartially() { return this._isAcceptingPartially; } @@ -194,7 +194,7 @@ export class InlineCompletionsModel extends Disposable { }); } - const cursorPosition = this._primaryPosition.get(); + const cursorPosition = this.primaryPosition.get(); if (changeSummary.dontRefetch) { return Promise.resolve(true); } @@ -257,7 +257,7 @@ export class InlineCompletionsModel extends Disposable { private readonly _inlineCompletionItems = derivedOpts({ owner: this }, reader => { const c = this._source.inlineCompletions.read(reader); if (!c) { return undefined; } - const cursorPosition = this._primaryPosition.read(reader); + const cursorPosition = this.primaryPosition.read(reader); let inlineEdit: InlineCompletionWithUpdatedRange | undefined = undefined; const visibleCompletions: InlineCompletionWithUpdatedRange[] = []; for (const completion of c.inlineCompletions) { @@ -355,10 +355,10 @@ export class InlineCompletionsModel extends Disposable { let edit = item.inlineEdit.toSingleTextEdit(reader); edit = singleTextRemoveCommonPrefix(edit, model); - const cursorPos = this._primaryPosition.read(reader); + const cursorPos = this.primaryPosition.read(reader); const cursorAtInlineEdit = LineRange.fromRangeInclusive(edit.range).addMargin(1, 1).contains(cursorPos.lineNumber); - const cursorDist = LineRange.fromRange(edit.range).distanceToLine(this._primaryPosition.read(reader).lineNumber); + const cursorDist = LineRange.fromRange(edit.range).distanceToLine(this.primaryPosition.read(reader).lineNumber); if (this._onlyShowWhenCloseToCursor.read(reader) && cursorDist > 3 && !item.inlineEdit.request.isExplicitRequest && !this._inAcceptFlow.read(reader)) { return undefined; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineDiffView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineDiffView.ts index faddbf04b09..38343311a3e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineDiffView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineDiffView.ts @@ -23,7 +23,7 @@ import { classNames } from './utils.js'; export interface IOriginalEditorInlineDiffViewState { diff: DetailedLineRangeMapping[]; modifiedText: AbstractText; - mode: 'mixedLines' | 'interleavedLines' | 'sideBySide'; + mode: 'mixedLines' | 'ghostText' | 'interleavedLines' | 'sideBySide'; modifiedCodeEditor: ICodeEditor; } @@ -111,7 +111,7 @@ export class OriginalEditorInlineDiffView extends Disposable { if (!diff) { return undefined; } const modified = diff.modifiedText; - const showInline = diff.mode === 'mixedLines'; + const showInline = diff.mode === 'mixedLines' || diff.mode === 'ghostText'; const showEmptyDecorations = true; @@ -214,7 +214,7 @@ export class OriginalEditorInlineDiffView extends Disposable { description: 'inserted-text', before: { content: insertedText, - inlineClassName: 'inlineCompletions-char-insert', + inlineClassName: diff.mode === 'ghostText' ? 'ghost-text-decoration' : 'inlineCompletions-char-insert', }, zIndex: 2, showIfCollapsed: true, diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts index 391cf7516b7..945c91b9e13 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts @@ -4,22 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { derived, IObservable } from '../../../../../../base/common/observable.js'; +import { derived, IObservable, IReader } from '../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../common/core/lineRange.js'; +import { Position } from '../../../../../common/core/position.js'; import { StringText } from '../../../../../common/core/textEdit.js'; import { DetailedLineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { TextModel } from '../../../../../common/model/textModel.js'; -import './view.css'; +import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import { IInlineEditsIndicatorState, InlineEditsIndicator } from './indicatorView.js'; import { IOriginalEditorInlineDiffViewState, OriginalEditorInlineDiffView } from './inlineDiffView.js'; +import { InlineEditsSideBySideDiff } from './sideBySideDiff.js'; import { applyEditToModifiedRangeMappings, createReindentEdit } from './utils.js'; -import { IInlineEditsIndicatorState, InlineEditsIndicator } from './indicatorView.js'; -import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import './view.css'; import { InlineEditWithChanges } from './viewAndDiffProducer.js'; -import { InlineEditsSideBySideDiff } from './sideBySideDiff.js'; export class InlineEditsView extends Disposable { private readonly _editorObs = observableCodeEditor(this._editor); @@ -37,7 +38,7 @@ export class InlineEditsView extends Disposable { } private readonly _uiState = derived<{ - state: 'collapsed' | 'mixedLines' | 'interleavedLines' | 'sideBySide'; + state: 'collapsed' | 'mixedLines' | 'ghostText' | 'interleavedLines' | 'sideBySide'; diff: DetailedLineRangeMapping[]; edit: InlineEditWithChanges; newText: string; @@ -55,17 +56,7 @@ export class InlineEditsView extends Disposable { let newText = edit.edit.apply(edit.originalText); let diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); - let state: 'collapsed' | 'mixedLines' | 'interleavedLines' | 'sideBySide'; - if (edit.isCollapsed) { - state = 'collapsed'; - } else if (diff.every(m => OriginalEditorInlineDiffView.supportsInlineDiffRendering(m)) && - (this._useMixedLinesDiff.read(reader) === 'whenPossible' || (edit.userJumpedToIt && this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible'))) { - state = 'mixedLines'; - } else if ((this._useInterleavedLinesDiff.read(reader) === 'always' || (edit.userJumpedToIt && this._useInterleavedLinesDiff.read(reader) === 'afterJump'))) { - state = 'interleavedLines'; - } else { - state = 'sideBySide'; - } + const state = this.determinRenderState(edit, reader, diff); if (state === 'sideBySide') { const indentationAdjustmentEdit = createReindentEdit(newText, edit.modifiedLineRange); @@ -140,4 +131,57 @@ export class InlineEditsView extends Disposable { }), this._model, )); + + private determinRenderState(edit: InlineEditWithChanges, reader: IReader, diff: DetailedLineRangeMapping[]) { + if (edit.isCollapsed) { + return 'collapsed'; + } + + if ( + (this._useMixedLinesDiff.read(reader) === 'whenPossible' || (edit.userJumpedToIt && this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible')) + && diff.every(m => OriginalEditorInlineDiffView.supportsInlineDiffRendering(m)) + ) { + return 'mixedLines'; + } + + if ( + this._useMixedLinesDiff.read(reader) === 'forStableInsertions' + && isInsertionAfterPosition(diff, edit.cursorPosition) + ) { + return 'ghostText'; + } + + if (this._useInterleavedLinesDiff.read(reader) === 'always' || (edit.userJumpedToIt && this._useInterleavedLinesDiff.read(reader) === 'afterJump')) { + return 'interleavedLines'; + } + + return 'sideBySide'; + } +} + +function isInsertionAfterPosition(diff: DetailedLineRangeMapping[], position: Position | null) { + if (!position) { + return false; + } + const pos = position; + + return diff.every(m => m.innerChanges!.every(r => isStableWordInsertion(r))); + + function isStableWordInsertion(r: RangeMapping) { + if (!r.originalRange.isEmpty()) { + return false; + } + const isInsertionWithinLine = r.modifiedRange.startLineNumber === r.modifiedRange.endLineNumber; + if (!isInsertionWithinLine) { + return false; + } + const insertPosition = r.originalRange.getStartPosition(); + if (pos.isBeforeOrEqual(insertPosition)) { + return true; + } + if (insertPosition.lineNumber < pos.lineNumber) { + return true; + } + return false; + } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts index 3f31d826857..ed40c6c0edb 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts @@ -53,6 +53,8 @@ export class InlineEditsViewAndDiffProducer extends Disposable { }); private readonly _inlineEditPromise = derived | undefined>(this, (reader) => { + const model = this._model.read(reader); + if (!model) { return undefined; } const inlineEdit = this._edit.read(reader); if (!inlineEdit) { return undefined; } @@ -86,7 +88,7 @@ export class InlineEditsViewAndDiffProducer extends Disposable { )); const diffEdits = new TextEdit(edits); - return new InlineEditWithChanges(text, diffEdits, inlineEdit.isCollapsed, inlineEdit.renderExplicitly, inlineEdit.commands, inlineEdit.inlineCompletion); //inlineEdit.showInlineIfPossible); + return new InlineEditWithChanges(text, diffEdits, inlineEdit.isCollapsed, model.primaryPosition.get(), inlineEdit.renderExplicitly, inlineEdit.commands, inlineEdit.inlineCompletion); //inlineEdit.showInlineIfPossible); }); }); @@ -116,6 +118,7 @@ export class InlineEditWithChanges { public readonly originalText: AbstractText, public readonly edit: TextEdit, public readonly isCollapsed: boolean, + public readonly cursorPosition: Position, public readonly userJumpedToIt: boolean, public readonly commands: readonly Command[], public readonly inlineCompletion: InlineCompletionItem, @@ -126,6 +129,7 @@ export class InlineEditWithChanges { return this.originalText.getValue() === other.originalText.getValue() && this.edit.equals(other.edit) && this.isCollapsed === other.isCollapsed && + this.cursorPosition.equals(other.cursorPosition) && this.userJumpedToIt === other.userJumpedToIt && this.commands === other.commands && this.inlineCompletion === other.inlineCompletion; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index eba21bfb561..77cd7ae49a5 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4602,7 +4602,7 @@ declare namespace monaco.editor { edits?: { experimental?: { enabled?: boolean; - useMixedLinesDiff?: 'never' | 'whenPossible' | 'afterJumpWhenPossible'; + useMixedLinesDiff?: 'never' | 'whenPossible' | 'forStableInsertions' | 'afterJumpWhenPossible'; useInterleavedLinesDiff?: 'never' | 'always' | 'afterJump'; onlyShowWhenCloseToCursor?: boolean; }; From afd7c3ccb38f580dced00040f65b6eae719c7908 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 13 Dec 2024 19:45:19 +0100 Subject: [PATCH 220/479] fixing incorrect hover size after resize of other hover (#236078) * fixing incorrect sizing after resize * replacing line --- src/vs/editor/contrib/hover/browser/contentHoverWidget.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts index ffb8aaa9f11..fdfc464bc04 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts @@ -138,6 +138,7 @@ export class ContentHoverWidget extends ResizableContentWidget { private _setHoverWidgetMaxDimensions(width: number | string, height: number | string): void { ContentHoverWidget._applyMaxDimensions(this._hover.contentsDomNode, width, height); + ContentHoverWidget._applyMaxDimensions(this._hover.scrollbar.getDomNode(), width, height); ContentHoverWidget._applyMaxDimensions(this._hover.containerDomNode, width, height); this._hover.containerDomNode.style.setProperty('--vscode-hover-maxWidth', typeof width === 'number' ? `${width}px` : width); this._layoutContentWidget(); @@ -145,9 +146,7 @@ export class ContentHoverWidget extends ResizableContentWidget { private _setAdjustedHoverWidgetDimensions(size: dom.Dimension): void { this._setHoverWidgetMaxDimensions('none', 'none'); - const width = size.width; - const height = size.height; - this._setHoverWidgetDimensions(width, height); + this._setHoverWidgetDimensions(size.width, size.height); } private _updateResizableNodeMaxDimensions(): void { @@ -297,6 +296,7 @@ export class ContentHoverWidget extends ResizableContentWidget { private _updateMaxDimensions() { const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastDimensions.height); const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 750, ContentHoverWidget._lastDimensions.width); + this._resizableNode.maxSize = new dom.Dimension(width, height); this._setHoverWidgetMaxDimensions(width, height); } @@ -304,7 +304,6 @@ export class ContentHoverWidget extends ResizableContentWidget { this._setRenderedHover(renderedHover); this._updateFont(); this._updateContent(renderedHover.domNode); - this._updateMaxDimensions(); this.onContentsChanged(); // Simply force a synchronous render on the editor // such that the widget does not really render with left = '0px' @@ -371,6 +370,7 @@ export class ContentHoverWidget extends ResizableContentWidget { const layoutInfo = this._editor.getLayoutInfo(); this._resizableNode.layout(layoutInfo.height, layoutInfo.width); this._setHoverWidgetDimensions('auto', 'auto'); + this._updateMaxDimensions(); } public setMinimumDimensions(dimensions: dom.Dimension): void { From afd3f0abf02675155a01770b7f45fcf3bb9202d6 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 13 Dec 2024 14:19:39 -0600 Subject: [PATCH 221/479] Check whether commands are executable before including them in terminal suggestions (#235997) --- .../src/terminalSuggestMain.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 04af5ff5da9..c7cdf75406e 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -168,6 +168,24 @@ function createCompletionItem(commandLine: string, cursorPosition: number, prefi }; } +async function isExecutable(filePath: string): Promise { + // Windows doesn't have the concept of an executable bit and running any + // file is possible. We considered using $PATHEXT here but since it's mostly + // there for legacy reasons and it would be easier and more intuitive to add + // a setting if needed instead. + if (osIsWindows()) { + return true; + } + try { + const stats = await fs.stat(filePath); + // On macOS/Linux, check if the executable bit is set + return (stats.mode & 0o100) !== 0; + } catch (error) { + // If the file does not exist or cannot be accessed, it's not executable + return false; + } +} + async function getCommandsInPath(): Promise | undefined> { if (cachedAvailableCommands) { return cachedAvailableCommands; @@ -176,7 +194,7 @@ async function getCommandsInPath(): Promise | undefined> { if (!paths) { return; } - + const pathSeparator = osIsWindows() ? '\\' : '/'; const executables = new Set(); for (const path of paths) { try { @@ -187,7 +205,7 @@ async function getCommandsInPath(): Promise | undefined> { const files = await vscode.workspace.fs.readDirectory(vscode.Uri.file(path)); for (const [file, fileType] of files) { - if (fileType !== vscode.FileType.Unknown && fileType !== vscode.FileType.Directory) { + if (fileType !== vscode.FileType.Unknown && fileType !== vscode.FileType.Directory && await isExecutable(path + pathSeparator + file)) { executables.add(file); } } From c1189b5a8e38e66a154eb07e28957c8beff587d0 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 13 Dec 2024 14:45:20 -0600 Subject: [PATCH 222/479] add aria label for disable/enable current file context button (#236077) fix #236076 --- .../chat/browser/attachments/implicitContextAttachment.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts b/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts index 73ab2960823..dfff8cee32b 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts @@ -79,6 +79,7 @@ export class ImplicitContextAttachmentWidget extends Disposable { this._register(this.hoverService.setupManagedHover(getDefaultHoverDelegate('element'), hintElement, title)); const buttonMsg = this.attachment.enabled ? localize('disable', "Disable current file context") : localize('enable', "Enable current file context"); + this.domNode.ariaLabel = buttonMsg; const toggleButton = this.renderDisposables.add(new Button(this.domNode, { supportIcons: true, title: buttonMsg })); toggleButton.icon = this.attachment.enabled ? Codicon.eye : Codicon.eyeClosed; this.renderDisposables.add(toggleButton.onDidClick((e) => { From 899afc031137585826b56aa200aca155a785a319 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:01:05 +0100 Subject: [PATCH 223/479] Git - ignore changes reported to the submodule folder (#205125) (#236098) --- extensions/git/src/repository.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 73a54cef4af..2465bd80bc4 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -2385,7 +2385,16 @@ export class Repository implements Disposable { } switch (raw.y) { - case 'M': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; + case 'M': { + // https://git-scm.com/docs/git-status#_porcelain_format_version_1 + // When using `-z` with the porcelain v1 format any submodule changes + // are reported as modified M instead of m or single ?. Due to that we + // will ignore any changes reported for the submodule folder. + if (this.submodules.every(s => !pathEquals(s.path, raw.path))) { + workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); + } + break; + } case 'D': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; case 'A': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; case 'R': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_RENAME, useIcons, renameUri)); break; From f7cbfef520f09f4c36c9f510a4f9c67e3f2c0cc9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 13 Dec 2024 13:26:02 -0800 Subject: [PATCH 224/479] extensions: warn folks not to change the debug worker name :P (#236104) Refs #232544 --- .../extensions/worker/webWorkerExtensionHostIframe.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html index 88407e89f16..372f26d51e9 100644 --- a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html @@ -4,7 +4,7 @@ @@ -12,6 +12,8 @@ (function () { const searchParams = new URL(document.location.href).searchParams; const vscodeWebWorkerExtHostId = searchParams.get('vscodeWebWorkerExtHostId') || ''; + // DO NOT CHANGE the name of the worker without also updating js-debug, as that + // is used to filter targets to attach to (e.g. #232544) const name = searchParams.get('debugged') ? 'DebugExtensionHostWorker' : 'ExtensionHostWorker'; const parentOrigin = searchParams.get('parentOrigin') || window.origin; const salt = searchParams.get('salt'); From af9502d4b1425e076e05b02850363b227bf9316e Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 13 Dec 2024 13:38:11 -0800 Subject: [PATCH 225/479] fix: don't cut off focus border for chat editing Add Files button (#236102) --- src/vs/workbench/contrib/chat/browser/chatInputPart.ts | 2 +- src/vs/workbench/contrib/chat/browser/media/chat.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 78b8a6628af..2c3d2e0a19b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -1282,7 +1282,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge })); dom.append(addFilesElement, button.element); - // REALTED files (after Add Files...) + // RELATED files (after Add Files...) for (const [uri, metadata] of chatEditingSession.workingSet) { if (metadata.state !== WorkingSetEntryState.Suggested) { continue; diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index b93329f8ddf..f9065621637 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -661,6 +661,7 @@ have to be updated for changes to the rules above, or to support more deeply nes } .interactive-session .chat-editing-session .chat-editing-session-toolbar-actions .monaco-button.secondary:first-child { + margin: 3px 0px 3px 3px; flex-shrink: 0; } From c921e57456cb5721151fcef69e21f73a1cb221c5 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 13 Dec 2024 13:47:11 -0800 Subject: [PATCH 226/479] fix: add aria label for chat working set (#236106) fix: add aria label for working set region --- src/vs/workbench/contrib/chat/browser/chatInputPart.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 2c3d2e0a19b..5ee4df94945 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -1166,6 +1166,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } else { suggestedFilesInWorkingSetCount = entries.filter(e => e.kind === 'reference' && e.state === WorkingSetEntryState.Suggested).length; } + overviewTitle.ariaLabel = overviewTitle.textContent; + overviewTitle.tabIndex = 0; if (excludedEntries.length > 0) { overviewFileCount.textContent = ' ' + localize('chatEditingSession.excludedFiles', '({0}/{1} files)', this.chatEditingService.editingSessionFileLimit + excludedEntries.length, this.chatEditingService.editingSessionFileLimit); From 0d676289ff74ffd00571a4dcce4ef80af2dfefda Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 13 Dec 2024 14:40:29 -0800 Subject: [PATCH 227/479] fix: reduce race timeout for getting chat related files (#236108) --- .../contrib/chat/browser/contrib/chatInputCompletions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 06a7bd3c912..bf5dfa67cef 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -564,7 +564,7 @@ class BuiltinDynamicCompletions extends Disposable { // RELATED FILES if (widget.location === ChatAgentLocation.EditingSession && widget.viewModel && this._chatEditingService.currentEditingSessionObs.get()?.chatSessionId === widget.viewModel?.sessionId) { - const relatedFiles = (await raceTimeout(this._chatEditingService.getRelatedFiles(widget.viewModel.sessionId, widget.getInput(), token), 1000)) ?? []; + const relatedFiles = (await raceTimeout(this._chatEditingService.getRelatedFiles(widget.viewModel.sessionId, widget.getInput(), token), 200)) ?? []; for (const relatedFileGroup of relatedFiles) { for (const relatedFile of relatedFileGroup.files) { if (seen.has(relatedFile.uri)) { From eb70dfe409b18664d5fafe9d4bcf1ff8fbbade68 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 13 Dec 2024 15:20:34 -0800 Subject: [PATCH 228/479] testing: fix transparent background in test error badges artifacting (#236109) --- .../contrib/testing/browser/media/testing.css | 2 -- .../contrib/testing/browser/theme.ts | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index cafaaac4dd7..2ebd1fc35bc 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -414,7 +414,6 @@ .monaco-workbench .test-error-content-widget .inner { margin-left: 20px; color: var(--vscode-testing-message-error-badgeForeground) !important; - background: var(--vscode-testing-message-error-badgeBackground); border-top-right-radius: 2px; border-bottom-right-radius: 2px; padding-right: 3px; @@ -452,7 +451,6 @@ top: -1px; width: 15px; left: -10px; - fill: var(--vscode-testing-message-error-badgeBackground); z-index: -1; stroke-width: 0.71px; /* 1 / sqrt(2) */ stroke: var(--vscode-testing-message-error-badgeBorder); diff --git a/src/vs/workbench/contrib/testing/browser/theme.ts b/src/vs/workbench/contrib/testing/browser/theme.ts index 439e1f765fa..b41f5828a97 100644 --- a/src/vs/workbench/contrib/testing/browser/theme.ts +++ b/src/vs/workbench/contrib/testing/browser/theme.ts @@ -183,7 +183,6 @@ export const testStatesToRetiredIconColors: { [K in TestResultState]?: string } registerThemingParticipant((theme, collector) => { const editorBg = theme.getColor(editorBackground); - const missBadgeBackground = editorBg && theme.getColor(testingUncoveredBackground)?.transparent(2).makeOpaque(editorBg); collector.addRule(` .coverage-deco-inline.coverage-deco-hit.coverage-deco-hovered { @@ -194,9 +193,22 @@ registerThemingParticipant((theme, collector) => { background: ${theme.getColor(testingUncoveredBackground)?.transparent(1.3)}; outline-color: ${theme.getColor(testingUncoveredBorder)?.transparent(2)}; } - .coverage-deco-branch-miss-indicator::before { - border-color: ${missBadgeBackground?.transparent(1.3)}; - background-color: ${missBadgeBackground}; + `); + + if (editorBg) { + const missBadgeBackground = theme.getColor(testingUncoveredBackground)?.transparent(2).makeOpaque(editorBg); + const errorBadgeBackground = theme.getColor(messageBadgeBackground)?.makeOpaque(editorBg); + collector.addRule(` + .coverage-deco-branch-miss-indicator::before { + border-color: ${missBadgeBackground?.transparent(1.3)}; + background-color: ${missBadgeBackground}; + } + .monaco-workbench .test-error-content-widget .inner{ + background: ${errorBadgeBackground}; + } + .monaco-workbench .test-error-content-widget .inner .arrow svg { + fill: ${errorBadgeBackground}; + } + `); } - `); }); From cdec0c6a14aeeae336c3467a7fa156f2cde6945f Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 13 Dec 2024 15:53:23 -0800 Subject: [PATCH 229/479] testing: improve performance of filtering file tests (#236112) Fixes #235819. Probably. --- .../common/mainThreadTestCollection.ts | 7 ++- .../contrib/testing/common/testService.ts | 45 ++++++++++++++----- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts b/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts index 1a691a3c80f..bff443281f6 100644 --- a/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts +++ b/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.ts @@ -5,6 +5,7 @@ import { Emitter } from '../../../../base/common/event.js'; import { Iterable } from '../../../../base/common/iterator.js'; +import { LinkedList } from '../../../../base/common/linkedList.js'; import { ResourceMap } from '../../../../base/common/map.js'; import { URI } from '../../../../base/common/uri.js'; import { IMainThreadTestCollection } from './testService.js'; @@ -184,8 +185,10 @@ export class MainThreadTestCollection extends AbstractIncrementalTestCollection< } private *getIterator() { - const queue = [this.rootIds]; - while (queue.length) { + const queue = new LinkedList>(); + queue.push(this.rootIds); + + while (queue.size > 0) { for (const id of queue.pop()!) { const node = this.getNodeById(id)!; yield node; diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index fe1d04bc441..f75e3145ae8 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -8,6 +8,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Event } from '../../../../base/common/event.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { LinkedList } from '../../../../base/common/linkedList.js'; import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { IObservable } from '../../../../base/common/observable.js'; import { IPrefixTreeNode, WellDefinedPrefixTree } from '../../../../base/common/prefixTree.js'; @@ -172,24 +173,44 @@ const waitForTestToBeIdle = (testService: ITestService, test: IncrementalTestCol * in strictly descending order. */ export const testsInFile = async function* (testService: ITestService, ident: IUriIdentityService, uri: URI, waitForIdle = true): AsyncIterable { - for (const test of testService.collection.all) { - if (!test.item.uri) { - continue; - } + const queue = new LinkedList>(); - if (ident.extUri.isEqual(uri, test.item.uri)) { - yield test; - } + const existing = [...testService.collection.getNodeByUrl(uri)]; + queue.push(existing.length ? existing.map(e => e.item.extId) : testService.collection.rootIds); - if (ident.extUri.isEqualOrParent(uri, test.item.uri)) { - if (test.expand === TestItemExpandState.Expandable) { - await testService.collection.expand(test.item.extId, 1); + let n = 0; + while (queue.size > 0) { + for (const id of queue.pop()!) { + n++; + const test = testService.collection.getNodeById(id); + if (!test) { + continue; // possible because we expand async and things could delete + } + + if (!test.item.uri) { + queue.push(test.children); + continue; + } + + if (ident.extUri.isEqual(uri, test.item.uri)) { + yield test; } - if (waitForIdle) { - await waitForTestToBeIdle(testService, test); + + if (ident.extUri.isEqualOrParent(uri, test.item.uri)) { + if (test.expand === TestItemExpandState.Expandable) { + await testService.collection.expand(test.item.extId, 1); + } + if (waitForIdle) { + await waitForTestToBeIdle(testService, test); + } + + if (test.children.size) { + queue.push(test.children); + } } } } + console.log('iterated', n, 'times'); }; /** From 31092ac1a0f9e68026cd0796f184fd3dfe978069 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 13 Dec 2024 17:40:57 -0800 Subject: [PATCH 230/479] debug: fix remote attach fails without message (#236117) Fixes #235722 --- src/vs/workbench/contrib/debug/node/debugAdapter.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 7c2b2e68307..4892338a1c1 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -114,6 +114,11 @@ export abstract class NetworkDebugAdapter extends StreamDebugAdapter { }); this.socket.on('error', error => { + // On ipv6 posix this can be an AggregateError which lacks a message. Use the first. + if (error instanceof AggregateError) { + error = error.errors[0]; + } + if (connected) { this._onError.fire(error); } else { From 1dc517c0a43a1df7730023b68533f9e3158b648d Mon Sep 17 00:00:00 2001 From: Jawad Najar <113301432+janajar@users.noreply.github.com> Date: Sat, 14 Dec 2024 09:39:46 -0500 Subject: [PATCH 231/479] Fix: When no results in search editor it throws an error (#235031) * Fix: issue #233200 Added a null check for matchRange in the iterateThroughMatches function to ensure setSelection is only called with valid arguments. * Revert package.json and package-lock.json to original states --------- Co-authored-by: Aly Ayyob <113083026+aayyob@users.noreply.github.com> --- src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 12423a7f38f..841b309d364 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -454,6 +454,7 @@ export class SearchEditor extends AbstractTextCodeEditor if (!matchRanges) { return; } const matchRange = (reverse ? findPrevRange : findNextRange)(matchRanges, currentPosition); + if (!matchRange) { return; } this.searchResultEditor.setSelection(matchRange); this.searchResultEditor.revealLineInCenterIfOutsideViewport(matchRange.startLineNumber); From f7c3b1b4748ba07ddf14dafd9b1dc611372c1bde Mon Sep 17 00:00:00 2001 From: notoriousmango Date: Sun, 15 Dec 2024 00:01:28 +0900 Subject: [PATCH 232/479] use openDocumentLink --- extensions/markdown-language-features/preview-src/index.ts | 6 +++--- .../markdown-language-features/src/commands/openImage.ts | 3 +-- .../src/preview/previewManager.ts | 5 +++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index cd2221b140d..1c2e2f5535a 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -11,6 +11,7 @@ import { SettingsManager, getData } from './settings'; import throttle = require('lodash.throttle'); import morphdom from 'morphdom'; import type { ToWebviewMessage } from '../types/previewMessaging'; +import { isOfScheme, Schemes } from '../src/util/schemes'; let scrollDisabledCount = 0; @@ -132,9 +133,8 @@ function addImageContexts() { for (const img of images) { img.id = 'image-' + idNumber; idNumber += 1; - const imageSource = img.src; - const imgSrcAttribute = img.getAttribute('src'); - const isLocalFile = imageSource !== imgSrcAttribute; + const imageSource = img.getAttribute('data-src'); + const isLocalFile = imageSource && !(isOfScheme(Schemes.http, imageSource) || isOfScheme(Schemes.https, imageSource)); const webviewSection = isLocalFile ? 'localImage' : 'image'; img.setAttribute('data-vscode-context', JSON.stringify({ webviewSection, id: img.id, 'preventDefaultContextMenuItems': true, resource: documentResource, imageSource })); } diff --git a/extensions/markdown-language-features/src/commands/openImage.ts b/extensions/markdown-language-features/src/commands/openImage.ts index 53f615148f3..64b1831df0d 100644 --- a/extensions/markdown-language-features/src/commands/openImage.ts +++ b/extensions/markdown-language-features/src/commands/openImage.ts @@ -16,7 +16,6 @@ export class OpenImageCommand implements Command { public execute(args: { resource: string; imageSource: string }) { const source = vscode.Uri.parse(args.resource); - const imageSourceUri = vscode.Uri.file(vscode.Uri.parse(args.imageSource).path); - vscode.commands.executeCommand('vscode.open', imageSourceUri, this._webviewManager.findPreview(source)); + this._webviewManager.openDocumentLink(args.imageSource, source); } } diff --git a/extensions/markdown-language-features/src/preview/previewManager.ts b/extensions/markdown-language-features/src/preview/previewManager.ts index 3b46c2a4322..3aa126da063 100644 --- a/extensions/markdown-language-features/src/preview/previewManager.ts +++ b/extensions/markdown-language-features/src/preview/previewManager.ts @@ -170,6 +170,11 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview } } + public openDocumentLink(linkText: string, fromResource: vscode.Uri) { + const viewColumn = this.findPreview(fromResource)?.resourceColumn; + return this._opener.openDocumentLink(linkText, fromResource, viewColumn); + } + public async deserializeWebviewPanel( webview: vscode.WebviewPanel, state: any From 7606e11e89e5cce00cfb0959c4d2d97ffd477472 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 14 Dec 2024 17:22:14 +0100 Subject: [PATCH 233/479] fix #235878 (#236129) --- .../common/extensionManagement.ts | 12 ++++++++++-- .../extensions/browser/extensionEditor.ts | 16 +++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 2d6713d409d..ba8a8e86fee 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -12,7 +12,7 @@ import { Platform } from '../../../base/common/platform.js'; import { URI } from '../../../base/common/uri.js'; import { localize2 } from '../../../nls.js'; import { ExtensionType, IExtension, IExtensionManifest, TargetPlatform } from '../../extensions/common/extensions.js'; -import { IFileService } from '../../files/common/files.js'; +import { FileOperationError, FileOperationResult, IFileService, IFileStat } from '../../files/common/files.js'; import { createDecorator } from '../../instantiation/common/instantiation.js'; export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9-A-Z]*)\\.([a-z0-9A-Z][a-z0-9-A-Z]*)$'; @@ -640,7 +640,15 @@ export interface IAllowedExtensionsService { } export async function computeSize(location: URI, fileService: IFileService): Promise { - const stat = await fileService.resolve(location); + let stat: IFileStat; + try { + stat = await fileService.resolve(location); + } catch (e) { + if ((e).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { + return 0; + } + throw e; + } if (stat.children) { const sizes = await Promise.all(stat.children.map(c => computeSize(c.resource, fileService))); return sizes.reduce((r, s) => r + s, 0); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index d5d8e0b3cd6..a47806e80f4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -1157,12 +1157,14 @@ class AdditionalDetailsWidget extends Disposable { $('div.more-info-entry-name', undefined, localize('id', "Identifier")), $('code', undefined, extension.identifier.id) )); - append(installInfo, - $('.more-info-entry', undefined, - $('div.more-info-entry-name', undefined, localize('Version', "Version")), - $('code', undefined, extension.manifest.version) - ) - ); + if (extension.type !== ExtensionType.System) { + append(installInfo, + $('.more-info-entry', undefined, + $('div.more-info-entry-name', undefined, localize('Version', "Version")), + $('code', undefined, extension.manifest.version) + ) + ); + } if (extension.installedTimestamp) { append(installInfo, $('.more-info-entry', undefined, @@ -1171,7 +1173,7 @@ class AdditionalDetailsWidget extends Disposable { ) ); } - if (extension.source !== 'gallery') { + if (!extension.isBuiltin && extension.source !== 'gallery') { const element = $('div', undefined, extension.source === 'vsix' ? localize('vsix', "VSIX") : localize('other', "Local")); append(installInfo, $('.more-info-entry', undefined, From 6fb1f6fbdd167ca4599f6ad28323257c3704a777 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 14 Dec 2024 17:22:39 +0100 Subject: [PATCH 234/479] fix #168779 (#236125) --- .../browser/userDataSyncWorkbenchService.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index ddfd1f0553f..6a1eb065c6c 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -6,7 +6,7 @@ import { IUserDataSyncService, IAuthenticationProvider, isAuthenticationProvider, IUserDataAutoSyncService, IUserDataSyncStoreManagementService, SyncStatus, IUserDataSyncEnablementService, IUserDataSyncResource, IResourcePreview, USER_DATA_SYNC_SCHEME, USER_DATA_SYNC_LOG_ID, } from '../../../../platform/userDataSync/common/userDataSync.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_CONFLICTS_VIEW_ID, CONTEXT_ENABLE_SYNC_CONFLICTS_VIEW, CONTEXT_HAS_CONFLICTS, IUserDataSyncConflictsView } from '../common/userDataSync.js'; +import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_CONFLICTS_VIEW_ID, CONTEXT_ENABLE_SYNC_CONFLICTS_VIEW, CONTEXT_HAS_CONFLICTS, IUserDataSyncConflictsView, getSyncAreaLabel } from '../common/userDataSync.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { getCurrentAuthenticationSessionInfo } from '../../authentication/browser/authenticationService.js'; @@ -403,9 +403,21 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private async handleConflictsWhileTurningOn(token: CancellationToken): Promise { + const conflicts = this.userDataSyncService.conflicts; + const andSeparator = localize('and', ' and '); + let conflictsText = ''; + for (let i = 0; i < conflicts.length; i++) { + if (i === conflicts.length - 1 && i !== 0) { + conflictsText += andSeparator; + } else if (i !== 0) { + conflictsText += ', '; + } + conflictsText += getSyncAreaLabel(conflicts[i].syncResource); + } + const singleConflictResource = conflicts.length === 1 ? getSyncAreaLabel(conflicts[0].syncResource) : undefined; await this.dialogService.prompt({ type: Severity.Warning, - message: localize('conflicts detected', "Conflicts Detected"), + message: localize('conflicts detected', "Conflicts Detected in {0}", conflictsText), detail: localize('resolve', "Please resolve conflicts to turn on..."), buttons: [ { @@ -417,11 +429,11 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } }, { - label: localize({ key: 'replace local', comment: ['&& denotes a mnemonic'] }, "Replace &&Local"), + label: singleConflictResource ? localize({ key: 'replace local single', comment: ['&& denotes a mnemonic'] }, "Accept &&Remote {0}", singleConflictResource) : localize({ key: 'replace local', comment: ['&& denotes a mnemonic'] }, "Accept &&Remote"), run: async () => this.replace(true) }, { - label: localize({ key: 'replace remote', comment: ['&& denotes a mnemonic'] }, "Replace &&Remote"), + label: singleConflictResource ? localize({ key: 'replace remote single', comment: ['&& denotes a mnemonic'] }, "Accept &&Local {0}", singleConflictResource) : localize({ key: 'replace remote', comment: ['&& denotes a mnemonic'] }, "Accept &&Local"), run: () => this.replace(false) }, ], From 402f07b34ce8aaa3997030c07999be8d8832057c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 15 Dec 2024 11:43:58 +0100 Subject: [PATCH 235/479] debt - remove `handleTitleDoubleClick` (#236154) Looks like this is not working anymore since app-region drag takes events away --- src/vs/platform/native/common/native.ts | 2 -- .../electron-main/nativeHostMainService.ts | 5 --- .../platform/window/electron-main/window.ts | 2 -- .../windows/electron-main/windowImpl.ts | 35 ------------------- .../test/electron-main/windowsFinder.test.ts | 1 - src/vs/workbench/electron-sandbox/window.ts | 20 ++--------- .../electron-sandbox/workbenchTestServices.ts | 1 - 7 files changed, 2 insertions(+), 64 deletions(-) diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 7ec630cb844..2fdfb63e0b5 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -83,8 +83,6 @@ export interface ICommonNativeHostService { isFullScreen(options?: INativeHostOptions): Promise; toggleFullScreen(options?: INativeHostOptions): Promise; - handleTitleDoubleClick(options?: INativeHostOptions): Promise; - getCursorScreenPoint(): Promise<{ readonly point: IPoint; readonly display: IRectangle }>; isMaximized(options?: INativeHostOptions): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 703f89ef66b..c3dbf1ee3ea 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -247,11 +247,6 @@ export class NativeHostMainService extends Disposable implements INativeHostMain window?.toggleFullScreen(); } - async handleTitleDoubleClick(windowId: number | undefined, options?: INativeHostOptions): Promise { - const window = this.windowById(options?.targetWindowId, windowId); - window?.handleTitleDoubleClick(); - } - async getCursorScreenPoint(windowId: number | undefined): Promise<{ readonly point: IPoint; readonly display: IRectangle }> { const point = screen.getCursorScreenPoint(); const display = screen.getDisplayNearestPoint(point); diff --git a/src/vs/platform/window/electron-main/window.ts b/src/vs/platform/window/electron-main/window.ts index 8a861e7f176..91a2eb1339a 100644 --- a/src/vs/platform/window/electron-main/window.ts +++ b/src/vs/platform/window/electron-main/window.ts @@ -34,8 +34,6 @@ export interface IBaseWindow extends IDisposable { setDocumentEdited(edited: boolean): void; isDocumentEdited(): boolean; - handleTitleDoubleClick(): void; - readonly isFullScreen: boolean; toggleFullScreen(): void; diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index 3a9132dd3cd..ab629987eae 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -314,41 +314,6 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { win.focus(); } - handleTitleDoubleClick(): void { - const win = this.win; - if (!win) { - return; - } - - // Respect system settings on mac with regards to title click on windows title - if (isMacintosh) { - const action = electron.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string'); - switch (action) { - case 'Minimize': - win.minimize(); - break; - case 'None': - break; - case 'Maximize': - default: - if (win.isMaximized()) { - win.unmaximize(); - } else { - win.maximize(); - } - } - } - - // Linux/Windows: just toggle maximize/minimized state - else { - if (win.isMaximized()) { - win.unmaximize(); - } else { - win.maximize(); - } - } - } - //#region Window Control Overlays private static readonly windowControlHeightStateStorageKey = 'windowControlHeight'; diff --git a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts index 98973497b19..c0889db322b 100644 --- a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts +++ b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts @@ -70,7 +70,6 @@ suite('WindowsFinder', () => { getRepresentedFilename(): string | undefined { throw new Error('Method not implemented.'); } setDocumentEdited(edited: boolean): void { throw new Error('Method not implemented.'); } isDocumentEdited(): boolean { throw new Error('Method not implemented.'); } - handleTitleDoubleClick(): void { throw new Error('Method not implemented.'); } updateTouchBar(items: UriDto[][]): void { throw new Error('Method not implemented.'); } serializeWindowState(): IWindowState { throw new Error('Method not implemented'); } updateWindowControls(options: { height?: number | undefined; backgroundColor?: string | undefined; foregroundColor?: string | undefined }): void { throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index ec924899b8b..c186b45d2ba 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -8,7 +8,7 @@ import { localize } from '../../nls.js'; import { URI } from '../../base/common/uri.js'; import { onUnexpectedError } from '../../base/common/errors.js'; import { equals } from '../../base/common/objects.js'; -import { EventType, EventHelper, addDisposableListener, ModifierKeyEmitter, getActiveElement, hasWindow, getWindow, getWindowById, getWindows } from '../../base/browser/dom.js'; +import { EventType, EventHelper, addDisposableListener, ModifierKeyEmitter, getActiveElement, hasWindow, getWindowById, getWindows } from '../../base/browser/dom.js'; import { Action, Separator, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../base/common/actions.js'; import { IFileService } from '../../platform/files/common/files.js'; import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors, IResourceDiffEditorInput, IUntypedEditorInput, IEditorPane, isResourceEditorInput, IResourceMergeEditorInput } from '../common/editor.js'; @@ -41,13 +41,12 @@ import { WorkbenchState, IWorkspaceContextService } from '../../platform/workspa import { coalesce } from '../../base/common/arrays.js'; import { ConfigurationTarget, IConfigurationService } from '../../platform/configuration/common/configuration.js'; import { IStorageService, StorageScope, StorageTarget } from '../../platform/storage/common/storage.js'; -import { assertIsDefined } from '../../base/common/types.js'; import { IOpenerService, IResolvedExternalUri, OpenOptions } from '../../platform/opener/common/opener.js'; import { Schemas } from '../../base/common/network.js'; import { INativeHostService } from '../../platform/native/common/native.js'; import { posix } from '../../base/common/path.js'; import { ITunnelService, RemoteTunnel, extractLocalHostUriMetaDataForPortMapping, extractQueryLocalHostUriMetaDataForPortMapping } from '../../platform/tunnel/common/tunnel.js'; -import { IWorkbenchLayoutService, Parts, positionFromString, Position } from '../services/layout/browser/layoutService.js'; +import { IWorkbenchLayoutService, positionFromString, Position } from '../services/layout/browser/layoutService.js'; import { IWorkingCopyService } from '../services/workingCopy/common/workingCopyService.js'; import { WorkingCopyCapabilities } from '../services/workingCopy/common/workingCopy.js'; import { IFilesConfigurationService } from '../services/filesConfiguration/common/filesConfigurationService.js'; @@ -397,21 +396,6 @@ export class NativeWindow extends BaseWindow { this._register(this.editorGroupService.onDidCreateAuxiliaryEditorPart(part => this.handleRepresentedFilename(part))); } - // Maximize/Restore on doubleclick (for macOS custom title) - if (isMacintosh && !hasNativeTitlebar(this.configurationService)) { - this._register(Event.runAndSubscribe(this.layoutService.onDidAddContainer, ({ container, disposables }) => { - const targetWindow = getWindow(container); - const targetWindowId = targetWindow.vscodeWindowId; - const titlePart = assertIsDefined(this.layoutService.getContainer(targetWindow, Parts.TITLEBAR_PART)); - - disposables.add(addDisposableListener(titlePart, EventType.DBLCLICK, e => { - EventHelper.stop(e); - - this.nativeHostService.handleTitleDoubleClick({ targetWindowId }); - })); - }, { container: this.layoutService.mainContainer, disposables: this._store })); - } - // Document edited: indicate for dirty working copies this._register(this.workingCopyService.onDidChangeDirty(workingCopy => { const gotDirty = workingCopy.isDirty(); diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index c58ce20646c..9bc958141ff 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -94,7 +94,6 @@ export class TestNativeHostService implements INativeHostService { } async toggleFullScreen(): Promise { } - async handleTitleDoubleClick(): Promise { } async isMaximized(): Promise { return true; } async isFullScreen(): Promise { return true; } async maximizeWindow(): Promise { } From 8fca93f9e2f8f887fcafa35eabaff8623f1a084f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 15 Dec 2024 11:44:46 +0100 Subject: [PATCH 236/479] Web: `homeIndicator` not respected anymore (fix #171724) (#236155) --- .../browser/parts/titlebar/titlebarPart.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 717c0a0e955..69342beb96a 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -434,22 +434,20 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.centerContent = append(this.rootContainer, $('.titlebar-center')); this.rightContent = append(this.rootContainer, $('.titlebar-right')); - // App Icon (Native Windows/Linux and Web) - if (!isMacintosh && !isWeb && !hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { + // App Icon (Windows, Linux) / Home Indicator (Web) + const homeIndicator = this.environmentService.options?.homeIndicator; + const supportsAppIcon = isWindows || isLinux || (isWeb && homeIndicator && !this.isAuxiliary); + if (supportsAppIcon && !hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { this.appIcon = prepend(this.leftContent, $('a.window-appicon')); - // Web-only home indicator and menu (not for auxiliary windows) - if (!this.isAuxiliary && isWeb) { - const homeIndicator = this.environmentService.options?.homeIndicator; - if (homeIndicator) { - const icon: ThemeIcon = getIconRegistry().getIcon(homeIndicator.icon) ? { id: homeIndicator.icon } : Codicon.code; - - this.appIcon.setAttribute('href', homeIndicator.href); - this.appIcon.classList.add(...ThemeIcon.asClassNameArray(icon)); - this.appIconBadge = document.createElement('div'); - this.appIconBadge.classList.add('home-bar-icon-badge'); - this.appIcon.appendChild(this.appIconBadge); - } + // Web: home indicator + if (isWeb && homeIndicator) { + const icon: ThemeIcon = getIconRegistry().getIcon(homeIndicator.icon) ? { id: homeIndicator.icon } : Codicon.code; + this.appIcon.setAttribute('href', homeIndicator.href); + this.appIcon.classList.add(...ThemeIcon.asClassNameArray(icon)); + this.appIconBadge = document.createElement('div'); + this.appIconBadge.classList.add('home-bar-icon-badge'); + this.appIcon.appendChild(this.appIconBadge); } } From 5c525a4d5664e73eb4e28c4df4b29f3f1196b6f0 Mon Sep 17 00:00:00 2001 From: aslezar <97354675+aslezar@users.noreply.github.com> Date: Sun, 15 Dec 2024 16:54:08 +0530 Subject: [PATCH 237/479] fixed formatting --- cli/src/util/prereqs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs index dc469fd30cd..af48bf0a93a 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -271,9 +271,9 @@ fn check_for_sufficient_glibcxx_versions(contents: Vec) -> Result Option { LDD_VERSION_RE.captures(output).map(|m| SimpleSemver { - major: m.get(1).map_or(0, |s| u32_from_bytes(s.as_bytes())), - minor: m.get(2).map_or(0, |s| u32_from_bytes(s.as_bytes())), - patch: 0, + major: m.get(1).map_or(0, |s| u32_from_bytes(s.as_bytes())), + minor: m.get(2).map_or(0, |s| u32_from_bytes(s.as_bytes())), + patch: 0, }) } From 4325b8ce0dfccf8a146211c83e8f39795096e415 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Sun, 15 Dec 2024 18:13:05 +0100 Subject: [PATCH 238/479] Use console.error and console.warn for errors and warnings (#236048) --- src/vs/platform/log/common/log.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index e503417a554..7ce341461a4 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -432,7 +432,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { warn(message: string | Error, ...args: any[]): void { if (this.canLog(LogLevel.Warning)) { if (this.useColors) { - console.log('%c WARN', 'color: #993', message, ...args); + console.warn('%c WARN', 'color: #993', message, ...args); } else { console.log(message, ...args); } @@ -442,7 +442,7 @@ export class ConsoleLogger extends AbstractLogger implements ILogger { error(message: string, ...args: any[]): void { if (this.canLog(LogLevel.Error)) { if (this.useColors) { - console.log('%c ERR', 'color: #f33', message, ...args); + console.error('%c ERR', 'color: #f33', message, ...args); } else { console.error(message, ...args); } From ce50bd4876af457f64d83cfd956bc916535285f4 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Sun, 15 Dec 2024 18:17:17 +0100 Subject: [PATCH 239/479] Remove unused showLocal proposal (#236032) * Remove unused showLocal proposal * No local build error?? --- .../common/extensionsApiProposals.ts | 3 --- .../api/browser/mainThreadDialogs.ts | 3 +-- .../workbench/api/common/extHost.api.impl.ts | 2 +- src/vs/workbench/api/common/extHostDialogs.ts | 7 +------ src/vscode-dts/vscode.proposed.showLocal.d.ts | 19 ------------------- 5 files changed, 3 insertions(+), 31 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.showLocal.d.ts diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 9a6636005b3..bd35500287e 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -319,9 +319,6 @@ const _allApiProposals = { shareProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.shareProvider.d.ts', }, - showLocal: { - proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.showLocal.d.ts', - }, speech: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.speech.d.ts', }, diff --git a/src/vs/workbench/api/browser/mainThreadDialogs.ts b/src/vs/workbench/api/browser/mainThreadDialogs.ts index 4ab41cf2663..50f3a61b94d 100644 --- a/src/vs/workbench/api/browser/mainThreadDialogs.ts +++ b/src/vs/workbench/api/browser/mainThreadDialogs.ts @@ -7,7 +7,6 @@ import { URI } from '../../../base/common/uri.js'; import { MainThreadDiaglogsShape, MainContext, MainThreadDialogOpenOptions, MainThreadDialogSaveOptions } from '../common/extHost.protocol.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { IFileDialogService, IOpenDialogOptions, ISaveDialogOptions } from '../../../platform/dialogs/common/dialogs.js'; -import { Schemas } from '../../../base/common/network.js'; @extHostNamedCustomer(MainContext.MainThreadDialogs) export class MainThreadDialogs implements MainThreadDiaglogsShape { @@ -47,7 +46,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape { canSelectMany: options?.canSelectMany, defaultUri: options?.defaultUri ? URI.revive(options.defaultUri) : undefined, title: options?.title || undefined, - availableFileSystems: options?.allowUIResources ? [Schemas.vscodeRemote, Schemas.file] : [] + availableFileSystems: [] }; if (options?.filters) { result.filters = []; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 6e68860da3f..85ff1aa52b2 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -781,7 +781,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostQuickOpen.showInput(options, token); }, showOpenDialog(options) { - return extHostDialogs.showOpenDialog(extension, options); + return extHostDialogs.showOpenDialog(options); }, showSaveDialog(options) { return extHostDialogs.showSaveDialog(options); diff --git a/src/vs/workbench/api/common/extHostDialogs.ts b/src/vs/workbench/api/common/extHostDialogs.ts index 8e98769e19c..05b37776943 100644 --- a/src/vs/workbench/api/common/extHostDialogs.ts +++ b/src/vs/workbench/api/common/extHostDialogs.ts @@ -6,8 +6,6 @@ import type * as vscode from 'vscode'; import { URI } from '../../../base/common/uri.js'; import { MainContext, MainThreadDiaglogsShape, IMainContext } from './extHost.protocol.js'; -import { checkProposedApiEnabled } from '../../services/extensions/common/extensions.js'; -import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; export class ExtHostDialogs { @@ -17,10 +15,7 @@ export class ExtHostDialogs { this._proxy = mainContext.getProxy(MainContext.MainThreadDialogs); } - showOpenDialog(extension: IExtensionDescription, options?: vscode.OpenDialogOptions): Promise { - if (options?.allowUIResources) { - checkProposedApiEnabled(extension, 'showLocal'); - } + showOpenDialog(options?: vscode.OpenDialogOptions): Promise { return this._proxy.$showOpenDialog(options).then(filepaths => { return filepaths ? filepaths.map(p => URI.revive(p)) : undefined; }); diff --git a/src/vscode-dts/vscode.proposed.showLocal.d.ts b/src/vscode-dts/vscode.proposed.showLocal.d.ts deleted file mode 100644 index 9a9294f596b..00000000000 --- a/src/vscode-dts/vscode.proposed.showLocal.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// https://github.com/microsoft/vscode/issues/131138 - -declare module 'vscode' { - - export interface OpenDialogOptions { - /** - * Controls whether the dialog allows users to select local files via the "Show Local" button. - * Extensions that set this to `true` should check the scheme of the selected file. - * Resources with the `file` scheme come from the same extension host as the extension. - * Resources with the `vscode-local` scheme come from an extension host running in the same place as the UI. - */ - allowUIResources?: boolean; - } -} From 7432213b30d0c4a161d4fa8e934f21500bfbb990 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 16 Dec 2024 10:17:29 +0100 Subject: [PATCH 240/479] debt - remove home indicator (#236189) It was broken before and not shown, and there were no complains, so lets remove it for good. --- .../parts/titlebar/media/titlebarpart.css | 17 ------------- .../browser/parts/titlebar/titlebarPart.ts | 19 ++------------- src/vs/workbench/browser/web.api.ts | 24 ------------------- 3 files changed, 2 insertions(+), 58 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 0264e5460d4..d3c6da2f239 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -271,23 +271,6 @@ display: none; } -.monaco-workbench .part.titlebar > .titlebar-container .window-appicon > .home-bar-icon-badge { - position: absolute; - right: 9px; - bottom: 6px; - width: 8px; - height: 8px; - z-index: 1; - /* on top of home indicator */ - background-image: url('../../../media/code-icon.svg'); - background-repeat: no-repeat; - background-position: center center; - background-size: 8px; - pointer-events: none; - border-top: 1px solid transparent; - border-left: 1px solid transparent; -} - /* Window Controls Container */ .monaco-workbench .part.titlebar .window-controls-container { display: flex; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 69342beb96a..93a798fa708 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -15,7 +15,6 @@ import { IConfigurationService, IConfigurationChangeEvent } from '../../../../pl import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { IBrowserWorkbenchEnvironmentService } from '../../../services/environment/browser/environmentService.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER, WORKBENCH_BACKGROUND } from '../../../common/theme.js'; import { isMacintosh, isWindows, isLinux, isWeb, isNative, platformLocale } from '../../../../base/common/platform.js'; import { Color } from '../../../../base/common/color.js'; @@ -29,8 +28,6 @@ import { createActionViewItem, fillInActionBarActions as fillInActionBarActions import { Action2, IMenu, IMenuService, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IHostService } from '../../../services/host/browser/host.js'; -import { Codicon } from '../../../../base/common/codicons.js'; -import { getIconRegistry } from '../../../../platform/theme/common/iconRegistry.js'; import { WindowTitle } from './windowTitle.js'; import { CommandCenterControl } from './commandCenterControl.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; @@ -434,21 +431,9 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.centerContent = append(this.rootContainer, $('.titlebar-center')); this.rightContent = append(this.rootContainer, $('.titlebar-right')); - // App Icon (Windows, Linux) / Home Indicator (Web) - const homeIndicator = this.environmentService.options?.homeIndicator; - const supportsAppIcon = isWindows || isLinux || (isWeb && homeIndicator && !this.isAuxiliary); - if (supportsAppIcon && !hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { + // App Icon (Windows, Linux) + if ((isWindows || isLinux) && !hasNativeTitlebar(this.configurationService, this.titleBarStyle)) { this.appIcon = prepend(this.leftContent, $('a.window-appicon')); - - // Web: home indicator - if (isWeb && homeIndicator) { - const icon: ThemeIcon = getIconRegistry().getIcon(homeIndicator.icon) ? { id: homeIndicator.icon } : Codicon.code; - this.appIcon.setAttribute('href', homeIndicator.href); - this.appIcon.classList.add(...ThemeIcon.asClassNameArray(icon)); - this.appIconBadge = document.createElement('div'); - this.appIconBadge.classList.add('home-bar-icon-badge'); - this.appIcon.appendChild(this.appIconBadge); - } } // Draggable region that we can manipulate for #52522 diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index c2668a8ca6a..9ae723f51fd 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -332,11 +332,6 @@ export interface IWorkbenchConstructionOptions { //#region Branding - /** - * Optional home indicator to appear above the hamburger menu in the activity bar. - */ - readonly homeIndicator?: IHomeIndicator; - /** * Optional welcome banner to appear above the workbench. Can be dismissed by the * user. @@ -576,25 +571,6 @@ export interface ICommand { handler: (...args: any[]) => unknown; } -export interface IHomeIndicator { - - /** - * The link to open when clicking the home indicator. - */ - href: string; - - /** - * The icon name for the home indicator. This needs to be one of the existing - * icons from our Codicon icon set. For example `code`. - */ - icon: string; - - /** - * A tooltip that will appear while hovering over the home indicator. - */ - title: string; -} - export interface IWelcomeBanner { /** From 737e2ed44a2136e4e7b153ad79ebbb9d8c0558bc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 16 Dec 2024 10:18:00 +0100 Subject: [PATCH 241/479] Free SKU flow does not work with multiple accounts (fix microsoft/vscode-copilot#11323) (#236190) --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 2245da971d3..e814b6acb21 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -42,7 +42,7 @@ import { IWorkspaceContextService } from '../../../../platform/workspace/common/ import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; import { IActivityService, ProgressBadge } from '../../../services/activity/common/activity.js'; -import { AuthenticationSession, IAuthenticationService } from '../../../services/authentication/common/authentication.js'; +import { AuthenticationSession, IAuthenticationExtensionsService, IAuthenticationService } from '../../../services/authentication/common/authentication.js'; import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser/layoutService.js'; @@ -706,6 +706,7 @@ class ChatSetupController extends Disposable { private readonly requests: ChatSetupRequests, @ITelemetryService private readonly telemetryService: ITelemetryService, @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @IAuthenticationExtensionsService private readonly authenticationExtensionsService: IAuthenticationExtensionsService, @IViewsService private readonly viewsService: IViewsService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IProductService private readonly productService: IProductService, @@ -804,8 +805,11 @@ class ChatSetupController extends Disposable { session = await this.authenticationService.createSession(defaultChat.providerId, defaultChat.providerScopes[0]); entitlement = await this.requests.forceResolveEntitlement(session); + + this.authenticationExtensionsService.updateAccountPreference(defaultChat.extensionId, defaultChat.providerId, session.account); + this.authenticationExtensionsService.updateAccountPreference(defaultChat.chatExtensionId, defaultChat.providerId, session.account); } catch (error) { - // noop + this.logService.error(`[chat setup] signIn: error ${error}`); } if (!session) { From 85709a0cd67d7945213598e7f3d4d017c7fbbe32 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:28:00 +0100 Subject: [PATCH 242/479] SCM - only show visible quick diff providers in the quick diff widget (#236192) --- .../contrib/scm/browser/quickDiffWidget.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index 35771008fe6..8ac67f6cd53 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -305,7 +305,11 @@ class QuickDiffWidget extends PeekViewWidget { } private shouldUseDropdown(): boolean { - return this.model.getQuickDiffResults() + const visibleQuickDiffs = this.model.quickDiffs.filter(quickDiff => quickDiff.visible); + const visibleQuickDiffResults = this.model.getQuickDiffResults() + .filter(result => visibleQuickDiffs.some(quickDiff => quickDiff.label === result.label)); + + return visibleQuickDiffResults .filter(quickDiff => quickDiff.changes.length > 0).length > 1; } @@ -333,9 +337,12 @@ class QuickDiffWidget extends PeekViewWidget { protected override _fillHead(container: HTMLElement): void { super._fillHead(container, true); + const visibleQuickDiffs = this.model.quickDiffs.filter(quickDiff => quickDiff.visible); + this.dropdownContainer = dom.prepend(this._titleElement!, dom.$('.dropdown')); - this.dropdown = this.instantiationService.createInstance(QuickDiffPickerViewItem, new QuickDiffPickerBaseAction((event?: IQuickDiffSelectItem) => this.switchQuickDiff(event)), - this.model.quickDiffs.map(quickDiffer => quickDiffer.label), this.model.changes[this._index].label); + this.dropdown = this.instantiationService.createInstance(QuickDiffPickerViewItem, + new QuickDiffPickerBaseAction((event?: IQuickDiffSelectItem) => this.switchQuickDiff(event)), + visibleQuickDiffs.map(quickDiff => quickDiff.label), this.model.changes[this._index].label); this.dropdown.render(this.dropdownContainer); this.updateActions(); } From 276e24792198ecb7e83533d575568b66da722064 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:03:13 +0100 Subject: [PATCH 243/479] Align view and container actions (#236199) align view and container actions --- src/vs/workbench/browser/media/part.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css index f17465a5e4a..0cbc7346b60 100644 --- a/src/vs/workbench/browser/media/part.css +++ b/src/vs/workbench/browser/media/part.css @@ -78,6 +78,7 @@ background-size: 16px; background-position: center center; background-repeat: no-repeat; + padding: 2px; /* Make sure view actions and container actions align */ } .monaco-workbench .part > .title > .title-actions .action-label .label { From 8db8225de61cc748e0ae79154ce26610a9b8ec80 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 16 Dec 2024 11:05:31 +0100 Subject: [PATCH 244/479] Allow file level comments to start collapsed (#236201) Fixes https://github.com/microsoft/vscode-pull-request-github/issues/6465 --- .../contrib/comments/browser/commentThreadZoneWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index eb2a0396eee..87e7767a8bc 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -388,7 +388,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._disposables.add(this._commentThreadWidget.onDidResize(dimension => { this._refresh(dimension); })); - if ((this._commentThread.collapsibleState === languages.CommentThreadCollapsibleState.Expanded) || (range === undefined)) { + if (this._commentThread.collapsibleState === languages.CommentThreadCollapsibleState.Expanded) { this.show(this.arrowPosition(range), 2); } From 3a1e3f7f92057ecc563da275e58f18ad8fceb2ca Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 16 Dec 2024 11:16:31 +0100 Subject: [PATCH 245/479] Adds derived owners (#236204) --- .../browser/model/inlineCompletionsModel.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index 3f9e7c85161..65201933d14 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -419,7 +419,7 @@ export class InlineCompletionsModel extends Disposable { return 'noSuggestion'; }); - public readonly inlineCompletionState = derived(reader => { + public readonly inlineCompletionState = derived(this, reader => { const s = this.state.read(reader); if (!s || s.kind !== 'ghostText') { return undefined; @@ -430,7 +430,7 @@ export class InlineCompletionsModel extends Disposable { return s; }); - public readonly inlineEditState = derived(reader => { + public readonly inlineEditState = derived(this, reader => { const s = this.state.read(reader); if (!s || s.kind !== 'inlineEdit') { return undefined; @@ -438,7 +438,7 @@ export class InlineCompletionsModel extends Disposable { return s; }); - public readonly inlineEditAvailable = derived(reader => { + public readonly inlineEditAvailable = derived(this, reader => { const s = this.inlineEditState.read(reader); return !!s; }); From 92d2d5d984b89fd44f831c04612e89c8e59028e2 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 16 Dec 2024 11:20:39 +0100 Subject: [PATCH 246/479] Observables: Adds debug logging (#236205) --- .../base/common/observableInternal/logging.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/vs/base/common/observableInternal/logging.ts b/src/vs/base/common/observableInternal/logging.ts index 56f154e001e..0de37a858cb 100644 --- a/src/vs/base/common/observableInternal/logging.ts +++ b/src/vs/base/common/observableInternal/logging.ts @@ -105,6 +105,28 @@ export class ConsoleObservableLogger implements IObservableLogger { this.changedObservablesSets.get(derived)!.add(observable); return existingHandleChange.apply(derived, [observable, change]); }; + + const debugTrackUpdating = false; + if (debugTrackUpdating) { + const updating: IObservable[] = []; + (derived as any).__debugUpdating = updating; + + const existingBeginUpdate = derived.beginUpdate; + derived.beginUpdate = (obs) => { + updating.push(obs); + return existingBeginUpdate.apply(derived, [obs]); + }; + + const existingEndUpdate = derived.endUpdate; + derived.endUpdate = (obs) => { + const idx = updating.indexOf(obs); + if (idx === -1) { + console.error('endUpdate called without beginUpdate', derived.debugName, obs.debugName); + } + updating.splice(idx, 1); + return existingEndUpdate.apply(derived, [obs]); + }; + } } handleDerivedRecomputed(derived: Derived, info: IChangeInformation): void { From 864d5d8aff43a9ff242c25f1603888a5ef036f89 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:28:44 +0100 Subject: [PATCH 247/479] Revert "Git - ignore changes reported to the submodule folder (#205125) (#236098)" (#236206) This reverts commit 899afc031137585826b56aa200aca155a785a319. --- extensions/git/src/repository.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 2465bd80bc4..73a54cef4af 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -2385,16 +2385,7 @@ export class Repository implements Disposable { } switch (raw.y) { - case 'M': { - // https://git-scm.com/docs/git-status#_porcelain_format_version_1 - // When using `-z` with the porcelain v1 format any submodule changes - // are reported as modified M instead of m or single ?. Due to that we - // will ignore any changes reported for the submodule folder. - if (this.submodules.every(s => !pathEquals(s.path, raw.path))) { - workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); - } - break; - } + case 'M': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.MODIFIED, useIcons, renameUri)); break; case 'D': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; case 'A': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; case 'R': workingTreeGroup.push(new Resource(this.resourceCommandResolver, ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_RENAME, useIcons, renameUri)); break; From 27a8a26b36230b7956c58b313c9e432e685d3cf9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 16 Dec 2024 11:38:23 +0100 Subject: [PATCH 248/479] update area labels (#236207) --- .vscode/notebooks/my-work.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 7577d9626c8..b47a08b5a18 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -102,7 +102,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:code-lens -label:command-center -label:comments -label:config -label:context-keys -label:custom-editors -label:debug -label:debug-console -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-sticky-scroll-decorations -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet-parse -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extension-signature -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:icon-brand -label:icons-product -label:icons-widget -label:inlay-hints -label:inline-chat -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:javascript -label:json -label:json-sorting -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:multi-monitor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-code-actions -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-format -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-statusbar -label:notebook-sticky-scroll -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:panel-chat -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:quickpick-chat -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-desktop -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-search -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:system-context-menu -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:unc -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-documents -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-auxwindow -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-voice -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:error-list -label:winget" + "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:code-lens -label:command-center -label:comments -label:config -label:context-keys -label:custom-editors -label:debug -label:debug-console -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-sticky-scroll-decorations -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet-parse -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extension-signature -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:icon-brand -label:icons-product -label:icons-widget -label:inlay-hints -label:inline-chat -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:javascript -label:json -label:json-sorting -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:multi-monitor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-code-actions -label:notebook-commands -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-format -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-serialization -label:notebook-statusbar -label:notebook-sticky-scroll -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:panel-chat -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:quickpick-chat -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-desktop -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-search -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:system-context-menu -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:unc -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-documents -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-auxwindow -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-voice -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:error-list -label:winget -label:cross-file-editing -label:editor-refactor-preview" }, { "kind": 1, From 17320a061adef54978fa300f9395e809df9168e0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 16 Dec 2024 11:40:19 +0100 Subject: [PATCH 249/479] Fixing escape does not hide hover (#236200) * fixing escape does not work * making same fix --- .../hover/browser/contentHoverController.ts | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index 26a7115f3ad..f7831694948 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -83,18 +83,17 @@ export class ContentHoverController extends Disposable implements IEditorContrib sticky: hoverOpts.sticky, hidingDelay: hoverOpts.hidingDelay }; - if (hoverOpts.enabled) { - this._listenersStore.add(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); - this._listenersStore.add(this._editor.onMouseUp(() => this._onEditorMouseUp())); - this._listenersStore.add(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); - this._listenersStore.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); - this._listenersStore.add(this._editor.onMouseLeave((e) => this._onEditorMouseLeave(e))); - this._listenersStore.add(this._editor.onDidChangeModel(() => this._cancelSchedulerAndHide())); - this._listenersStore.add(this._editor.onDidChangeModelContent(() => this._cancelScheduler())); - this._listenersStore.add(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e))); - } else { + if (!hoverOpts.enabled) { this._cancelSchedulerAndHide(); } + this._listenersStore.add(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); + this._listenersStore.add(this._editor.onMouseUp(() => this._onEditorMouseUp())); + this._listenersStore.add(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); + this._listenersStore.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); + this._listenersStore.add(this._editor.onMouseLeave((e) => this._onEditorMouseLeave(e))); + this._listenersStore.add(this._editor.onDidChangeModel(() => this._cancelSchedulerAndHide())); + this._listenersStore.add(this._editor.onDidChangeModelContent(() => this._cancelScheduler())); + this._listenersStore.add(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e))); } private _unhookListeners(): void { @@ -229,9 +228,11 @@ export class ContentHoverController extends Disposable implements IEditorContrib if (!mouseEvent) { return; } - const contentWidget: ContentHoverWidgetWrapper = this._getOrCreateContentWidget(); - if (contentWidget.showsOrWillShow(mouseEvent)) { - return; + if (this._hoverSettings.enabled) { + const contentWidget: ContentHoverWidgetWrapper = this._getOrCreateContentWidget(); + if (contentWidget.showsOrWillShow(mouseEvent)) { + return; + } } if (_sticky) { return; From dc3bf2d878a01e7ef6e0031f59d5be363f8c0ab7 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:45:03 +0100 Subject: [PATCH 250/479] Git - fix submodule decoration (#236208) --- extensions/git/src/decorationProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index ddb3574890b..2e72a1e4114 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -118,11 +118,11 @@ class GitDecorationProvider implements FileDecorationProvider { private onDidRunGitStatus(): void { const newDecorations = new Map(); - this.collectSubmoduleDecorationData(newDecorations); this.collectDecorationData(this.repository.indexGroup, newDecorations); this.collectDecorationData(this.repository.untrackedGroup, newDecorations); this.collectDecorationData(this.repository.workingTreeGroup, newDecorations); this.collectDecorationData(this.repository.mergeGroup, newDecorations); + this.collectSubmoduleDecorationData(newDecorations); const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()])); this.decorations = newDecorations; From 002eaa541a29bba3f6a5194ce49047992c9d6a63 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 16 Dec 2024 12:11:06 +0100 Subject: [PATCH 251/479] remove console.log fyi @connor4312 (#236209) --- src/vs/workbench/contrib/testing/common/testService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index f75e3145ae8..4f9fbbd8e9a 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -210,7 +210,6 @@ export const testsInFile = async function* (testService: ITestService, ident: IU } } } - console.log('iterated', n, 'times'); }; /** From 1bbe4e9d96e0ee30a1fa5fdeb3aeddb947bae871 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:15:15 +0100 Subject: [PATCH 252/479] Focus panel if maximized otherwise editor (#236211) fix #205584 --- src/vs/workbench/browser/layout.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index ca6cb860397..b60d2864283 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1249,7 +1249,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } focus(): void { - this.focusPart(Parts.EDITOR_PART, getWindow(this.activeContainer)); + if (this.isPanelMaximized() && this.mainContainer === this.activeContainer) { + this.focusPart(Parts.PANEL_PART); + } else { + this.focusPart(Parts.EDITOR_PART, getWindow(this.activeContainer)); + } } private focusPanelOrEditor(): void { From 188b1f1b31cfd10448c19421bde0a528f41e13d5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 16 Dec 2024 03:15:28 -0800 Subject: [PATCH 253/479] Only add additional margin on hover children with bg Fixes #228136 --- src/vs/base/browser/ui/hover/hoverWidget.css | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/hover/hoverWidget.css b/src/vs/base/browser/ui/hover/hoverWidget.css index e6b088e3301..c8138b37c16 100644 --- a/src/vs/base/browser/ui/hover/hoverWidget.css +++ b/src/vs/base/browser/ui/hover/hoverWidget.css @@ -176,12 +176,23 @@ color: var(--vscode-textLink-activeForeground); } -/** Spans in markdown hovers need a margin-bottom to avoid looking cramped: https://github.com/microsoft/vscode/issues/101496 **/ -.monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents):not(.html-hover-contents) span { +/** + * Spans in markdown hovers need a margin-bottom to avoid looking cramped: + * https://github.com/microsoft/vscode/issues/101496 + + * This was later refined to only apply when the last child of a rendered markdown block (before the + * border or a `hr`) uses background color: + * https://github.com/microsoft/vscode/issues/228136 + */ +.monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents):not(.html-hover-contents) p:last-child [style*="background-color"] { margin-bottom: 4px; display: inline-block; } +/** + * Add a slight margin to try vertically align codicons with any text + * https://github.com/microsoft/vscode/issues/221359 + */ .monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents):not(.html-hover-contents) span.codicon { margin-bottom: 2px; } From 0c995148f461418349157adad18d38eb0987a5c8 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 16 Dec 2024 13:27:29 +0100 Subject: [PATCH 254/479] Do not inherit outer editor padding (#236219) --- .../inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index cf7c1f94cf4..096070fb167 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -235,6 +235,7 @@ export class InlineEditsSideBySideDiff extends Disposable { bracketPairsHorizontal: false, highlightActiveIndentation: false, }, + padding: { top: 0, bottom: 0 }, folding: false, selectOnLineNumbers: false, selectionHighlight: false, From 7abd349c802187e8fe6231bfc6755c43f34c73ef Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:10:34 +0100 Subject: [PATCH 255/479] Hide Panel (X) tooltip now includes key binding (#236062) * #209193 * fix glitching render --- .../parts/auxiliarybar/auxiliaryBarActions.ts | 13 +++- .../browser/parts/panel/panelActions.ts | 61 ++++--------------- 2 files changed, 21 insertions(+), 53 deletions(-) diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts index 575a3aefe8f..3bea858332e 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts @@ -11,12 +11,13 @@ import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js' import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { AuxiliaryBarVisibleContext } from '../../../common/contextkeys.js'; import { ViewContainerLocation, ViewContainerLocationToString } from '../../../common/views.js'; -import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser/layoutService.js'; +import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts } from '../../../services/layout/browser/layoutService.js'; import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js'; import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { SwitchCompositeViewAction } from '../compositeBarActions.js'; +import { closeIcon } from '../panel/panelActions.js'; const auxiliaryBarRightIcon = registerIcon('auxiliarybar-right-layout-icon', Codicon.layoutSidebarRight, localize('toggleAuxiliaryIconRight', 'Icon to toggle the auxiliary bar off in its right position.')); const auxiliaryBarRightOffIcon = registerIcon('auxiliarybar-right-off-layout-icon', Codicon.layoutSidebarRightOff, localize('toggleAuxiliaryIconRightOn', 'Icon to toggle the auxiliary bar on in its right position.')); @@ -34,10 +35,11 @@ export class ToggleAuxiliaryBarAction extends Action2 { title: ToggleAuxiliaryBarAction.LABEL, toggled: { condition: AuxiliaryBarVisibleContext, - title: localize('secondary sidebar', "Secondary Side Bar"), + title: localize('closeSecondarySideBar', 'Hide Secondary Side Bar'), + icon: closeIcon, mnemonicTitle: localize({ key: 'secondary sidebar mnemonic', comment: ['&& denotes a mnemonic'] }, "Secondary Si&&de Bar"), }, - + icon: closeIcon, // Ensures no flickering when using toggled.icon category: Categories.View, f1: true, keybinding: { @@ -54,6 +56,11 @@ export class ToggleAuxiliaryBarAction extends Action2 { id: MenuId.MenubarAppearanceMenu, group: '2_workbench_layout', order: 2 + }, { + id: MenuId.AuxiliaryBarTitle, + group: 'navigation', + order: 2, + when: ContextKeyExpr.equals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.DEFAULT) } ] }); diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index c9250e36e84..e8fbbacb68a 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -8,8 +8,8 @@ import { localize, localize2 } from '../../../../nls.js'; import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; import { MenuId, MenuRegistry, registerAction2, Action2, IAction2Options } from '../../../../platform/actions/common/actions.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { ActivityBarPosition, isHorizontal, IWorkbenchLayoutService, LayoutSettings, PanelAlignment, Parts, Position, positionToString } from '../../../services/layout/browser/layoutService.js'; -import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelPositionContext, PanelVisibleContext } from '../../../common/contextkeys.js'; +import { isHorizontal, IWorkbenchLayoutService, PanelAlignment, Parts, Position, positionToString } from '../../../services/layout/browser/layoutService.js'; +import { PanelAlignmentContext, PanelMaximizedContext, PanelPositionContext, PanelVisibleContext } from '../../../common/contextkeys.js'; import { ContextKeyExpr, ContextKeyExpression } from '../../../../platform/contextkey/common/contextkey.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; @@ -24,7 +24,7 @@ import { SwitchCompositeViewAction } from '../compositeBarActions.js'; const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, localize('maximizeIcon', 'Icon to maximize a panel.')); const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, localize('restoreIcon', 'Icon to restore a panel.')); -const closeIcon = registerIcon('panel-close', Codicon.close, localize('closeIcon', 'Icon to close a panel.')); +export const closeIcon = registerIcon('panel-close', Codicon.close, localize('closeIcon', 'Icon to close a panel.')); const panelIcon = registerIcon('panel-layout-icon', Codicon.layoutPanel, localize('togglePanelOffIcon', 'Icon to toggle the panel off when it is on.')); const panelOffIcon = registerIcon('panel-layout-icon-off', Codicon.layoutPanelOff, localize('togglePanelOnIcon', 'Icon to toggle the panel on when it is off.')); @@ -39,9 +39,11 @@ export class TogglePanelAction extends Action2 { title: TogglePanelAction.LABEL, toggled: { condition: PanelVisibleContext, - title: localize('toggle panel', "Panel"), + title: localize('closePanel', 'Hide Panel'), + icon: closeIcon, mnemonicTitle: localize({ key: 'toggle panel mnemonic', comment: ['&& denotes a mnemonic'] }, "&&Panel"), }, + icon: closeIcon, // Ensures no flickering when using toggled.icon f1: true, category: Categories.View, keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyJ, weight: KeybindingWeight.WorkbenchContrib }, @@ -54,7 +56,11 @@ export class TogglePanelAction extends Action2 { id: MenuId.LayoutControlMenuSubmenu, group: '0_workbench_layout', order: 4 - }, + }, { + id: MenuId.PanelTitle, + group: 'navigation', + order: 2 + } ] }); } @@ -288,51 +294,6 @@ registerAction2(class extends Action2 { } }); -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'workbench.action.closePanel', - title: localize2('closePanel', 'Hide Panel'), - category: Categories.View, - icon: closeIcon, - menu: [{ - id: MenuId.CommandPalette, - when: PanelVisibleContext, - }, { - id: MenuId.PanelTitle, - group: 'navigation', - order: 2 - }] - }); - } - run(accessor: ServicesAccessor) { - accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.PANEL_PART); - } -}); - -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'workbench.action.closeAuxiliaryBar', - title: localize2('closeSecondarySideBar', 'Hide Secondary Side Bar'), - category: Categories.View, - icon: closeIcon, - menu: [{ - id: MenuId.CommandPalette, - when: AuxiliaryBarVisibleContext, - }, { - id: MenuId.AuxiliaryBarTitle, - group: 'navigation', - order: 2, - when: ContextKeyExpr.equals(`config.${LayoutSettings.ACTIVITY_BAR_LOCATION}`, ActivityBarPosition.DEFAULT) - }] - }); - } - run(accessor: ServicesAccessor) { - accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.AUXILIARYBAR_PART); - } -}); - MenuRegistry.appendMenuItems([ { id: MenuId.LayoutControlMenu, From 9b05bdafb3d816d25a23b392fbcf24b6a7285087 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 16 Dec 2024 15:11:31 +0100 Subject: [PATCH 256/479] * make sure to cancel request when disposing session (#236225) * make sure to stop editing when request is cancalled fixes https://github.com/microsoft/vscode-copilot/issues/11349 fixes https://github.com/microsoft/vscode-copilot-release/issues/2684 --- .../contrib/chat/browser/actions/chatClearActions.ts | 4 ++-- .../contrib/chat/browser/chatEditing/chatEditingService.ts | 2 -- .../contrib/chat/browser/chatEditing/chatEditingSession.ts | 7 +++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index cbb333f9656..07f9ce1cc93 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -188,7 +188,7 @@ export function registerNewChatActions() { announceChatCleared(accessibilitySignalService); const widget = widgetService.getWidgetBySessionId(context.sessionId); if (widget) { - chatEditingService.currentEditingSessionObs.get()?.stop(true); + await chatEditingService.currentEditingSessionObs.get()?.stop(true); widget.clear(); widget.attachmentModel.clear(); widget.focusInput(); @@ -199,7 +199,7 @@ export function registerNewChatActions() { const widget = chatView.widget; announceChatCleared(accessibilitySignalService); - chatEditingService.currentEditingSessionObs.get()?.stop(true); + await chatEditingService.currentEditingSessionObs.get()?.stop(true); widget.clear(); widget.attachmentModel.clear(); widget.focusInput(); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts index af6ffc0640e..4d064636e7a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingService.ts @@ -328,8 +328,6 @@ export class ChatEditingService extends Disposable implements IChatEditingServic if (responseModel.isComplete) { onResponseComplete(responseModel); disposable.dispose(); - } else if (responseModel.isCanceled || responseModel.isStale) { - disposable.dispose(); } }); } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index 6f24595cbad..31e9a672a13 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts @@ -6,7 +6,7 @@ import { ITask, Sequencer, timeout } from '../../../../../base/common/async.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { Emitter } from '../../../../../base/common/event.js'; -import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, dispose } from '../../../../../base/common/lifecycle.js'; import { ResourceMap, ResourceSet } from '../../../../../base/common/map.js'; import { autorun, derived, IObservable, IReader, ITransaction, observableValue, transaction } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; @@ -523,10 +523,9 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio override dispose() { this._assertNotDisposed(); - for (const entry of this._entriesObs.get()) { - entry.dispose(); - } + this._chatService.cancelCurrentRequestForSession(this.chatSessionId); + dispose(this._entriesObs.get()); super.dispose(); this._state.set(ChatEditingSessionState.Disposed, undefined); this._onDidDispose.fire(); From 845f0ac17b88e75ed4ff2afd6a9975f36b1d7cb5 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 16 Dec 2024 15:48:58 +0100 Subject: [PATCH 257/479] Refactoring hover controller (#236228) refactoring hover computation --- .../hover/browser/contentHoverController.ts | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index f7831694948..e44c40a947d 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -61,7 +61,11 @@ export class ContentHoverController extends Disposable implements IEditorContrib ) { super(); this._reactToEditorMouseMoveRunner = this._register(new RunOnceScheduler( - () => this._reactToEditorMouseMove(this._mouseMoveEvent), 0 + () => { + if (this._mouseMoveEvent) { + this._reactToEditorMouseMove(this._mouseMoveEvent); + } + }, 0 )); this._hookListeners(); this._register(this._editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => { @@ -155,33 +159,49 @@ export class ContentHoverController extends Disposable implements IEditorContrib this.hideContentHover(); } - private _shouldNotRecomputeCurrentHoverWidget(mouseEvent: IEditorMouseEvent): boolean { - + private _shouldKeepCurrentHover(mouseEvent: IEditorMouseEvent): boolean { + const contentWidget = this._contentWidget; + if (!contentWidget) { + return false; + } const isHoverSticky = this._hoverSettings.sticky; - const isMouseOnStickyContentHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean): boolean => { const isMouseOnContentHoverWidget = this._isMouseOnContentHoverWidget(mouseEvent); return isHoverSticky && isMouseOnContentHoverWidget; }; - const isMouseOnColorPicker = (mouseEvent: IEditorMouseEvent): boolean => { + const isMouseOnColorPickerOrChoosingColor = (mouseEvent: IEditorMouseEvent): boolean => { + const isColorPickerVisible = contentWidget.isColorPickerVisible; const isMouseOnContentHoverWidget = this._isMouseOnContentHoverWidget(mouseEvent); - const isColorPickerVisible = this._contentWidget?.isColorPickerVisible ?? false; - return isMouseOnContentHoverWidget && isColorPickerVisible; + const isMouseOnHoverWithColorPicker = isColorPickerVisible && isMouseOnContentHoverWidget; + const isMaybeChoosingColor = isColorPickerVisible && this._isMouseDown; + return isMouseOnHoverWithColorPicker || isMaybeChoosingColor; }; // TODO@aiday-mar verify if the following is necessary code const isTextSelectedWithinContentHoverWidget = (mouseEvent: IEditorMouseEvent, sticky: boolean): boolean => { - return (sticky - && this._contentWidget?.containsNode(mouseEvent.event.browserEvent.view?.document.activeElement) - && !mouseEvent.event.browserEvent.view?.getSelection()?.isCollapsed) ?? false; + const view = mouseEvent.event.browserEvent.view; + if (!view) { + return false; + } + return sticky && contentWidget.containsNode(view.document.activeElement) && !view.getSelection()?.isCollapsed; }; - return isMouseOnStickyContentHoverWidget(mouseEvent, isHoverSticky) - || isMouseOnColorPicker(mouseEvent) + const isFocused = contentWidget.isFocused; + const isResizing = contentWidget.isResizing; + const isStickyAndVisibleFromKeyboard = this._hoverSettings.sticky && contentWidget.isVisibleFromKeyboard; + + return this.shouldKeepOpenOnEditorMouseMoveOrLeave + || isFocused + || isResizing + || isStickyAndVisibleFromKeyboard + || isMouseOnStickyContentHoverWidget(mouseEvent, isHoverSticky) + || isMouseOnColorPickerOrChoosingColor(mouseEvent) || isTextSelectedWithinContentHoverWidget(mouseEvent, isHoverSticky); } private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void { - const shouldReactToEditorMouseMove = this._shouldReactToEditorMouseMove(mouseEvent); - if (!shouldReactToEditorMouseMove) { + this._mouseMoveEvent = mouseEvent; + const shouldKeepCurrentHover = this._shouldKeepCurrentHover(mouseEvent); + if (shouldKeepCurrentHover) { + this._reactToEditorMouseMoveRunner.cancel(); return; } const shouldRescheduleHoverComputation = this._shouldRescheduleHoverComputation(); @@ -194,28 +214,6 @@ export class ContentHoverController extends Disposable implements IEditorContrib this._reactToEditorMouseMove(mouseEvent); } - private _shouldReactToEditorMouseMove(mouseEvent: IEditorMouseEvent): boolean { - if (this.shouldKeepOpenOnEditorMouseMoveOrLeave) { - return false; - } - this._mouseMoveEvent = mouseEvent; - if (this._contentWidget && (this._contentWidget.isFocused || this._contentWidget.isResizing || this._isMouseDown && this._contentWidget.isColorPickerVisible)) { - return false; - } - const sticky = this._hoverSettings.sticky; - if (sticky && this._contentWidget?.isVisibleFromKeyboard) { - // Sticky mode is on and the hover has been shown via keyboard - // so moving the mouse has no effect - return false; - } - const shouldNotRecomputeCurrentHoverWidget = this._shouldNotRecomputeCurrentHoverWidget(mouseEvent); - if (shouldNotRecomputeCurrentHoverWidget) { - this._reactToEditorMouseMoveRunner.cancel(); - return false; - } - return true; - } - private _shouldRescheduleHoverComputation(): boolean { const hidingDelay = this._hoverSettings.hidingDelay; const isContentHoverWidgetVisible = this._contentWidget?.isVisible ?? false; @@ -224,10 +222,7 @@ export class ContentHoverController extends Disposable implements IEditorContrib return isContentHoverWidgetVisible && this._hoverSettings.sticky && hidingDelay > 0; } - private _reactToEditorMouseMove(mouseEvent: IEditorMouseEvent | undefined): void { - if (!mouseEvent) { - return; - } + private _reactToEditorMouseMove(mouseEvent: IEditorMouseEvent): void { if (this._hoverSettings.enabled) { const contentWidget: ContentHoverWidgetWrapper = this._getOrCreateContentWidget(); if (contentWidget.showsOrWillShow(mouseEvent)) { From 386d58d0dec05465e66185197ef6eb626ad3fb78 Mon Sep 17 00:00:00 2001 From: aslezar <97354675+aslezar@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:49:16 +0530 Subject: [PATCH 258/479] fixed formattting error --- cli/src/util/prereqs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs index af48bf0a93a..f8eff34ec39 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -274,7 +274,7 @@ fn extract_ldd_version(output: &[u8]) -> Option { major: m.get(1).map_or(0, |s| u32_from_bytes(s.as_bytes())), minor: m.get(2).map_or(0, |s| u32_from_bytes(s.as_bytes())), patch: 0, - }) + }) } #[allow(dead_code)] From 6338f2920cea263c97ed153afa68378906e20c81 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:21:01 +0100 Subject: [PATCH 259/479] SCM - quick diff decorators use legancy diff algorithm to avoid touching diffs (#236231) --- .../contrib/scm/browser/quickDiffModel.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index e7f97e04082..9907a627c27 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -22,7 +22,7 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { ISplice } from '../../../../base/common/sequence.js'; import { DiffState } from '../../../../editor/browser/widget/diffEditor/diffEditorViewModel.js'; import { toLineChanges } from '../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; -import { LineRangeMapping, lineRangeMappingFromChange } from '../../../../editor/common/diff/rangeMapping.js'; +import { LineRangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; import { IDiffEditorModel } from '../../../../editor/common/editorCommon.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; @@ -273,16 +273,14 @@ export class QuickDiffModel extends Disposable { } private async _diff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<{ changes: readonly IChange[] | null; changes2: readonly LineRangeMapping[] | null }> { - if (this.options?.algorithm === undefined) { - const changes = await this.editorWorkerService.computeDirtyDiff(original, modified, ignoreTrimWhitespace); - return { changes, changes2: changes?.map(change => lineRangeMappingFromChange(change)) ?? null }; - } + // When no algorithm is specified, we use the legacy diff algorithm along with a 1000ms + // timeout as the diff information is being used for the quick diff editor decorations + const algorithm = this.options?.algorithm ?? 'legacy'; + const maxComputationTimeMs = this.options?.algorithm ? Number.MAX_SAFE_INTEGER : 1000; const result = await this.editorWorkerService.computeDiff(original, modified, { - computeMoves: false, - ignoreTrimWhitespace, - maxComputationTimeMs: Number.MAX_SAFE_INTEGER - }, this.options.algorithm); + computeMoves: false, ignoreTrimWhitespace, maxComputationTimeMs + }, algorithm); return { changes: result ? toLineChanges(DiffState.fromDiffResult(result)) : null, changes2: result?.changes ?? null }; } From 3bbe4568920ffada9fba325985feb39e37bb9fb7 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:23:54 +0100 Subject: [PATCH 260/479] Polish tab bar focus styles (#236232) polish tab bar focus + :lipstick: --- .../parts/editor/media/multieditortabscontrol.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css index d0bb1d3d186..f70426f7d9f 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -50,7 +50,7 @@ display: none; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tab:not([tabIndex="0"]) .tabs-and-actions-container.tabs-border-bottom::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tab:not(.active) .tabs-and-actions-container.tabs-border-bottom::after { content: ''; position: absolute; bottom: 0; @@ -285,22 +285,22 @@ width: 100%; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.selected.tab-border-top > .tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top:not(:focus) > .tab-border-top-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.selected.tab-border-top:not(:focus) > .tab-border-top-container { z-index: 6; top: 0; height: 1px; background-color: var(--tab-border-top-color); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom:not([tabIndex="0"]) > .tab-border-bottom-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom:not(:focus) > .tab-border-bottom-container { z-index: 10; bottom: 0; height: 1px; background-color: var(--tab-border-bottom-color); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top:not(:focus) > .tab-border-top-container { z-index: 6; top: 0; height: 2px; From a31ec0d89aa656d684547b33d83c326b79342117 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:39:11 +0100 Subject: [PATCH 261/479] GitHub - do not show the "Publish to GitHub" command in an empty workspace (#236234) --- extensions/github/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/github/package.json b/extensions/github/package.json index ece19e32f54..e84b35d19b7 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -69,7 +69,7 @@ "commandPalette": [ { "command": "github.publish", - "when": "git-base.gitEnabled && remoteName != 'codespaces'" + "when": "git-base.gitEnabled && workspaceFolderCount != 0 && remoteName != 'codespaces'" }, { "command": "github.copyVscodeDevLink", From a58957af828dc2a936161e87c4c0ac0a29927e79 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 16 Dec 2024 16:39:38 +0100 Subject: [PATCH 262/479] fix https://github.com/microsoft/vscode/issues/232918 (#236233) --- .../contrib/inlineChat/browser/inlineChatStrategies.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index b77311ce72b..4d8b814e0df 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -416,6 +416,10 @@ export class LiveStrategy extends EditModeStrategy { continue; } const hunkRanges = candidate.hunk.getRangesN(); + if (hunkRanges.length === 0) { + // bogous hunk + continue; + } const myDistance = zoneLine <= hunkRanges[0].startLineNumber ? hunkRanges[0].startLineNumber - zoneLine : zoneLine - hunkRanges[0].endLineNumber; From cb279a8eb2aa96c58f0220bc94b0cde3740365ac Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 16 Dec 2024 16:55:47 +0100 Subject: [PATCH 263/479] Use position:fixed to make sure to be on top of everything in the workbench (#236236) --- .../view/inlineEdits/sideBySideDiff.ts | 19 ++++++++---- .../browser/view/inlineEdits/utils.ts | 29 +++++++++++++++++-- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 096070fb167..12e853ddcdf 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -114,7 +114,12 @@ export class InlineEditsSideBySideDiff extends Disposable { this._register(this._editorObs.createOverlayWidget({ domNode: this._overflowView.element, - position: constObservable(null), + position: constObservable({ + preference: { + top: 0, + left: 0 + } + }), allowEditorOverflow: true, minContentWidthInPx: constObservable(0), })); @@ -418,7 +423,14 @@ export class InlineEditsSideBySideDiff extends Disposable { } const stickyScrollHeight = this._stickyScrollHeight.read(reader); const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); - return top > stickyScrollHeight; + if (top <= stickyScrollHeight) { + return false; + } + const bottom = this._editor.getTopForLineNumber(range.endLineNumberExclusive) - this._editorObs.scrollTop.read(reader); + if (bottom >= this._editorObs.layoutInfo.read(reader).height) { + return false; + } + return true; }); @@ -567,10 +579,7 @@ export class InlineEditsSideBySideDiff extends Disposable { private readonly _overflowView = n.div({ class: 'inline-edits-view', style: { - position: 'absolute', overflow: 'visible', - top: '0px', - left: '0px', zIndex: '100', display: this._display, }, diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index 16971c2ebc6..af375cca816 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { h, isSVGElement } from '../../../../../../base/browser/dom.js'; +import { getDomNodePagePosition, h, isSVGElement } from '../../../../../../base/browser/dom.js'; import { KeybindingLabel, unthemedKeybindingLabelOptions } from '../../../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; import { numberComparator } from '../../../../../../base/common/arrays.js'; import { findFirstMin } from '../../../../../../base/common/arraysFind.js'; import { BugIndicatingError } from '../../../../../../base/common/errors.js'; -import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; -import { derived, IObservable, IReader } from '../../../../../../base/common/observable.js'; +import { Disposable, DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { derived, IObservable, IReader, observableValue, transaction } from '../../../../../../base/common/observable.js'; import { OS } from '../../../../../../base/common/platform.js'; import { getIndentationLength, splitLines } from '../../../../../../base/common/strings.js'; import { URI } from '../../../../../../base/common/uri.js'; @@ -426,3 +426,26 @@ export function mapOutFalsy(obs: IObservable): return obs as IObservable; }); } + +export function observeElementPosition(element: HTMLElement, store: DisposableStore) { + const topLeft = getDomNodePagePosition(element); + const top = observableValue('top', topLeft.top); + const left = observableValue('left', topLeft.left); + + const resizeObserver = new ResizeObserver(() => { + transaction(tx => { + const topLeft = getDomNodePagePosition(element); + top.set(topLeft.top, tx); + left.set(topLeft.left, tx); + }); + }); + + resizeObserver.observe(element); + + store.add(toDisposable(() => resizeObserver.disconnect())); + + return { + top, + left + }; +} From d3bd083824d2181e5b524696c3077afb7d770e1e Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 16 Dec 2024 17:20:04 +0100 Subject: [PATCH 264/479] Scroll in the preview editor when necessary (#236241) --- .../view/inlineEdits/sideBySideDiff.ts | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 12e853ddcdf..481af0ed288 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -145,9 +145,7 @@ export class InlineEditsSideBySideDiff extends Disposable { return; } - const editHeight = layoutInfo.editHeight; - const width = this._previewEditorWidth.read(reader); - this.previewEditor.layout({ height: editHeight, width }); + this.previewEditor.layout({ height: layoutInfo.editHeight, width: layoutInfo.previewEditorWidth }); const topEdit = layoutInfo.edit1; this._editorContainer.element.style.top = `${topEdit.y}px`; @@ -160,6 +158,15 @@ export class InlineEditsSideBySideDiff extends Disposable { this._elements.root.classList.toggle('toolbarDropdownVisible', toolbarDropdownVisible.read(reader)); }));*/ + this._register(autorun(reader => { + const layoutInfo = this._previewEditorLayoutInfo.read(reader); + if (!layoutInfo) { + return; + } + + this._previewEditorObs.editor.setScrollLeft(layoutInfo.desiredPreviewEditorScrollLeft); + })); + this._editorContainerTopLeft.set(this._previewEditorLayoutInfo.map(i => i?.edit1), undefined); } @@ -330,7 +337,7 @@ export class InlineEditsSideBySideDiff extends Disposable { /** * ![test](./layout.dio.svg) */ - private readonly _previewEditorLayoutInfo = derived(this, (reader) => {// + private readonly _previewEditorLayoutInfo = derived(this, (reader) => { const inlineEdit = this._edit.read(reader); if (!inlineEdit) { return null; @@ -346,11 +353,13 @@ export class InlineEditsSideBySideDiff extends Disposable { const editorContentMaxWidthInRange = maxContentWidthInRange(this._editorObs, state.originalDisplayRange, reader); const editorLayout = this._editorObs.layoutInfo.read(reader); - const previewWidth = this._previewEditorWidth.read(reader); + const previewContentWidth = this._previewEditorWidth.read(reader); const editorContentAreaWidth = editorLayout.contentWidth - editorLayout.verticalScrollbarWidth; - const clientContentAreaRight = editorLayout.contentLeft + editorLayout.contentWidth + this._editor.getContainerDomNode().getBoundingClientRect().left; + const editorBoundingClientRect = this._editor.getContainerDomNode().getBoundingClientRect(); + const clientContentAreaRight = editorLayout.contentLeft + editorLayout.contentWidth + editorBoundingClientRect.left; const remainingWidthRightOfContent = getWindow(this._editor.getContainerDomNode()).outerWidth - clientContentAreaRight; - const desiredMinimumWidth = Math.min(editorLayout.contentWidth * 0.3, previewWidth, 100); + const remainingWidthRightOfEditor = getWindow(this._editor.getContainerDomNode()).outerWidth - editorBoundingClientRect.right; + const desiredMinimumWidth = Math.min(editorLayout.contentWidth * 0.3, previewContentWidth, 100); const IN_EDITOR_DISPLACEMENT = 0; const maximumAvailableWidth = IN_EDITOR_DISPLACEMENT + remainingWidthRightOfContent; @@ -367,12 +376,20 @@ export class InlineEditsSideBySideDiff extends Disposable { ); const previewEditorLeftInTextArea = Math.min(editorContentMaxWidthInRange + 20, maxPreviewEditorLeft); - const previewEditorLeft = editorLayout.contentLeft + previewEditorLeftInTextArea; - const maxContentWidth = editorContentMaxWidthInRange + 20 + previewWidth + 70; + const maxContentWidth = editorContentMaxWidthInRange + 20 + previewContentWidth + 70; const dist = maxPreviewEditorLeft - previewEditorLeftInTextArea; - const left = Math.max(editorLayout.contentLeft, previewEditorLeft - horizontalScrollOffset); + let desiredPreviewEditorScrollLeft; + let left; + if (previewEditorLeftInTextArea > horizontalScrollOffset) { + desiredPreviewEditorScrollLeft = 0; + left = editorLayout.contentLeft + previewEditorLeftInTextArea - horizontalScrollOffset; + } else { + desiredPreviewEditorScrollLeft = horizontalScrollOffset - previewEditorLeftInTextArea; + left = editorLayout.contentLeft; + + } const selectionTop = this._originalVerticalStartPosition.read(reader) ?? this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); const selectionBottom = this._originalVerticalEndPosition.read(reader) ?? this._editor.getTopForLineNumber(range.endLineNumberExclusive) - this._editorObs.scrollTop.read(reader); @@ -394,6 +411,8 @@ export class InlineEditsSideBySideDiff extends Disposable { const codeEditDist = codeEditDistRange.clip(dist); const editHeight = this._editor.getOption(EditorOption.lineHeight) * inlineEdit.modifiedLineRange.length; + const previewEditorWidth = remainingWidthRightOfEditor + editorLayout.width - editorLayout.contentLeft - codeEditDist; + const edit1 = new Point(left + codeEditDist, selectionTop); const edit2 = new Point(left + codeEditDist, selectionTop + editHeight); @@ -407,9 +426,10 @@ export class InlineEditsSideBySideDiff extends Disposable { edit1, edit2, editHeight, - previewEditorLeft, maxContentWidth, shouldShowShadow: clipped, + desiredPreviewEditorScrollLeft, + previewEditorWidth }; }); @@ -437,7 +457,7 @@ export class InlineEditsSideBySideDiff extends Disposable { private readonly _extendedModifiedPath = derived(reader => { const layoutInfo = this._previewEditorLayoutInfo.read(reader); if (!layoutInfo) { return undefined; } - const width = this._previewEditorWidth.read(reader); + const width = layoutInfo.previewEditorWidth; const extendedModifiedPathBuilder = new PathBuilder() .moveTo(layoutInfo.code1) .lineTo(layoutInfo.edit1) From 4fa8dfa8e1299ac7dbf0da21bc04f3f0b055e64f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 16 Dec 2024 17:42:26 +0100 Subject: [PATCH 265/479] set `insertMode: replace` mode for snippets (#236245) --- extensions/json-language-features/package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index cb6bc601e1d..ff620435b98 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -139,6 +139,12 @@ "strings": true }, "editor.suggest.insertMode": "replace" + }, + "[snippets]": { + "editor.quickSuggestions": { + "strings": true + }, + "editor.suggest.insertMode": "replace" } }, "jsonValidation": [ From a6a6e8e3807465c735b27ccca3fa7f86bed34cad Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 16 Dec 2024 08:45:54 -0800 Subject: [PATCH 266/479] remove width and height attributes from multi-file-edits.svg (#236010) --- .../welcomeGettingStarted/common/media/multi-file-edits.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e151a4b95f2..9106074c4c0 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,4 +1,4 @@ - + From 7e8ac0f74fcfe7b56bc8159fde95b9e31af061e5 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:59:03 +0100 Subject: [PATCH 267/479] Fix titlebar update for longer extension names (#236235) fix #171742 --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 93a798fa708..3facaf51e23 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -536,6 +536,10 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { this.title.innerText = this.windowTitle.value; this.titleDisposables.add(this.windowTitle.onDidChange(() => { this.title.innerText = this.windowTitle.value; + // layout menubar and other renderings in the titlebar + if (this.lastLayoutDimensions) { + this.updateLayout(this.lastLayoutDimensions); + } })); } From 2008c6338cad30260023442561965fc5ec8a01c8 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 16 Dec 2024 18:23:19 +0100 Subject: [PATCH 268/479] Immediately re-render sticky scroll on pressing Shift key (#236244) immediately rerender --- .../browser/stickyScrollController.ts | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index f4ef1a671a9..38e4419456b 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -30,6 +30,7 @@ import { IMouseEvent, StandardMouseEvent } from '../../../../base/browser/mouseE import { FoldingController } from '../../folding/browser/folding.js'; import { FoldingModel, toggleCollapseState } from '../../folding/browser/foldingModel.js'; import { Emitter, Event } from '../../../../base/common/event.js'; +import { mainWindow } from '../../../../base/browser/window.js'; export interface IStickyScrollController { get stickyScrollCandidateProvider(): IStickyLineCandidateProvider; @@ -73,6 +74,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _endLineNumbers: number[] = []; private _showEndForLine: number | undefined; private _minRebuildFromLine: number | undefined; + private _mouseTarget: EventTarget | null = null; private readonly _onDidChangeStickyScrollHeight = this._register(new Emitter<{ height: number }>()); public readonly onDidChangeStickyScrollHeight = this._onDidChangeStickyScrollHeight.event; @@ -300,26 +302,26 @@ export class StickyScrollController extends Disposable implements IEditorContrib } this._revealPosition(position); })); - this._register(dom.addStandardDisposableListener(stickyScrollWidgetDomNode, dom.EventType.MOUSE_MOVE, (mouseEvent: IMouseEvent) => { - if (mouseEvent.shiftKey) { - const currentEndForLineIndex = this._stickyScrollWidget.getLineIndexFromChildDomNode(mouseEvent.target); - if (currentEndForLineIndex === null || this._showEndForLine !== null && this._showEndForLine === currentEndForLineIndex) { - return; - } - this._showEndForLine = currentEndForLineIndex; - this._renderStickyScroll(); - return; - } - if (this._showEndForLine !== undefined) { - this._showEndForLine = undefined; - this._renderStickyScroll(); - } - })); - this._register(dom.addDisposableListener(stickyScrollWidgetDomNode, dom.EventType.MOUSE_LEAVE, (e) => { + const mouseMoveListener = (mouseEvent: MouseEvent) => { + this._mouseTarget = mouseEvent.target; + this._onMouseMoveOrKeyDown(mouseEvent); + }; + const keyDownListener = (mouseEvent: KeyboardEvent) => { + this._onMouseMoveOrKeyDown(mouseEvent); + }; + const keyUpListener = (e: KeyboardEvent) => { if (this._showEndForLine !== undefined) { this._showEndForLine = undefined; this._renderStickyScroll(); } + }; + mainWindow.addEventListener(dom.EventType.MOUSE_MOVE, mouseMoveListener); + mainWindow.addEventListener(dom.EventType.KEY_DOWN, keyDownListener); + mainWindow.addEventListener(dom.EventType.KEY_UP, keyUpListener); + this._register(toDisposable(() => { + mainWindow.removeEventListener(dom.EventType.MOUSE_MOVE, mouseMoveListener); + mainWindow.removeEventListener(dom.EventType.KEY_DOWN, keyDownListener); + mainWindow.removeEventListener(dom.EventType.KEY_UP, keyUpListener); })); this._register(gesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, _keyboardEvent]) => { @@ -403,6 +405,21 @@ export class StickyScrollController extends Disposable implements IEditorContrib }); } + private _onMouseMoveOrKeyDown(mouseEvent: KeyboardEvent | MouseEvent): void { + if (!mouseEvent.shiftKey) { + return; + } + if (!this._mouseTarget || !dom.isHTMLElement(this._mouseTarget)) { + return; + } + const currentEndForLineIndex = this._stickyScrollWidget.getLineIndexFromChildDomNode(this._mouseTarget); + if (currentEndForLineIndex === null || this._showEndForLine === currentEndForLineIndex) { + return; + } + this._showEndForLine = currentEndForLineIndex; + this._renderStickyScroll(); + } + private _toggleFoldingRegionForLine(line: number | null) { if (!this._foldingModel || line === null) { return; From b7f7aeaf81b7e5710f015bd368b8311f6cb5f348 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 16 Dec 2024 18:33:46 +0100 Subject: [PATCH 269/479] std-in files (/tmp/code-stdin-*) are not removed on remote and should have user only access rights (#236251) --- src/vs/platform/environment/node/stdin.ts | 7 ++++++- src/vs/server/node/server.cli.ts | 13 ++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/environment/node/stdin.ts b/src/vs/platform/environment/node/stdin.ts index c3ecd62f953..93e8e9e5c1e 100644 --- a/src/vs/platform/environment/node/stdin.ts +++ b/src/vs/platform/environment/node/stdin.ts @@ -38,12 +38,17 @@ export function getStdinFilePath(): string { return randomPath(tmpdir(), 'code-stdin', 3); } +async function createStdInFile(targetPath: string) { + await fs.promises.appendFile(targetPath, ''); + await fs.promises.chmod(targetPath, 0o600); // Ensure the file is only read/writable by the user: https://github.com/microsoft/vscode-remote-release/issues/9048 +} + export async function readFromStdin(targetPath: string, verbose: boolean, onEnd?: Function): Promise { let [encoding, iconv] = await Promise.all([ resolveTerminalEncoding(verbose), // respect terminal encoding when piping into file import('@vscode/iconv-lite-umd'), // lazy load encoding module for usage - fs.promises.appendFile(targetPath, '') // make sure file exists right away (https://github.com/microsoft/vscode/issues/155341) + createStdInFile(targetPath) // make sure file exists right away (https://github.com/microsoft/vscode/issues/155341) ]); if (!iconv.default.encodingExists(encoding)) { diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index 324a92bf937..7f20588c3bd 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -184,10 +184,11 @@ export async function main(desc: ProductDescription, args: string[]): Promise | undefined; + let stdinFilePath: string | undefined; if (hasReadStdinArg && hasStdinWithoutTty()) { try { - let stdinFilePath = cliStdInFilePath; + stdinFilePath = cliStdInFilePath; if (!stdinFilePath) { stdinFilePath = getStdinFilePath(); const readFromStdinDone = new DeferredPromise(); @@ -351,8 +352,18 @@ export async function main(desc: ProductDescription, args: string[]): Promise Date: Mon, 16 Dec 2024 12:51:24 -0600 Subject: [PATCH 270/479] `onCommandExecuted`, hide suggest widget (#236261) fix #224492 --- .../terminalContrib/suggest/browser/terminalSuggestAddon.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index 83c87e8cec2..81476106cf2 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -115,6 +115,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._promptInputModel.onDidFinishInput(() => this.hideSuggestWidget()), ); } + this._register(commandDetection.onCommandExecuted(() => this.hideSuggestWidget())); } else { this._promptInputModel = undefined; } From 50dca635cb1ae36023399b4565223f234754f643 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 Dec 2024 13:02:45 -0600 Subject: [PATCH 271/479] add rm from context aria label (#236096) fix #236094 --- src/vs/base/browser/ui/button/button.ts | 20 +++++++++++++------ .../contrib/chat/browser/chatInputPart.ts | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 36e12cf9f7d..78fbad75c1d 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -28,7 +28,10 @@ import { IActionProvider } from '../dropdown/dropdown.js'; export interface IButtonOptions extends Partial { readonly title?: boolean | string; - readonly ariaLabel?: boolean | string; + /** + * Will fallback to `title` if not set. + */ + readonly ariaLabel?: string; readonly supportIcons?: boolean; readonly supportShortLabel?: boolean; readonly secondary?: boolean; @@ -261,11 +264,7 @@ export class Button extends Disposable implements IButton { this.setTitle(title); - if (typeof this.options.ariaLabel === 'string') { - this._element.setAttribute('aria-label', this.options.ariaLabel); - } else if (this.options.ariaLabel) { - this._element.setAttribute('aria-label', title); - } + this._setAriaLabel(); this._label = value; } @@ -286,7 +285,16 @@ export class Button extends Disposable implements IButton { } } + private _setAriaLabel(): void { + if (typeof this.options.ariaLabel === 'string') { + this._element.setAttribute('aria-label', this.options.ariaLabel); + } else if (typeof this.options.title === 'string') { + this._element.setAttribute('aria-label', this.options.title); + } + } + set icon(icon: ThemeIcon) { + this._setAriaLabel(); this._element.classList.add(...ThemeIcon.asClassNameArray(icon)); } diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 5ee4df94945..998fcc7afb3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -980,7 +980,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const clearButton = new Button(widget, { supportIcons: true, hoverDelegate, - title: localize('chat.attachment.clearButton', "Remove from context"), + title: localize('chat.attachment.clearButton', "Remove from context") }); // If this item is rendering in place of the last attached context item, focus the clear button so the user can continue deleting attached context items with the keyboard From 466eb0be759a7e098aa4d04780e71bb13566bbe2 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 Dec 2024 13:24:42 -0600 Subject: [PATCH 272/479] fix replacement index,length for terminal completions (#236260) fix bugs --- .../src/terminalSuggestMain.ts | 16 +++-- .../browser/terminalCompletionService.ts | 4 +- .../browser/terminalCompletionService.test.ts | 68 ++++++++++++++++++- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index c7cdf75406e..4eab00a8659 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -158,12 +158,14 @@ function getLabel(spec: Fig.Spec | Fig.Arg | Fig.Suggestion | string): string[] return spec.name; } -function createCompletionItem(commandLine: string, cursorPosition: number, prefix: string, label: string, description?: string, kind?: vscode.TerminalCompletionItemKind): vscode.TerminalCompletionItem { +function createCompletionItem(cursorPosition: number, prefix: string, label: string, description?: string, kind?: vscode.TerminalCompletionItemKind): vscode.TerminalCompletionItem { + const endsWithSpace = prefix.endsWith(' '); + const lastWord = endsWithSpace ? '' : prefix.split(' ').at(-1) ?? ''; return { label, detail: description ?? '', - replacementIndex: commandLine.length - prefix.length >= 0 ? commandLine.length - prefix.length : commandLine[cursorPosition - 1] === ' ' ? cursorPosition : cursorPosition - 1, - replacementLength: prefix.length, + replacementIndex: cursorPosition - lastWord.length, + replacementLength: lastWord.length > 0 ? lastWord.length : cursorPosition, kind: kind ?? vscode.TerminalCompletionItemKind.Method }; } @@ -268,7 +270,7 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon || !!firstCommand && specLabel.startsWith(firstCommand) ) { // push it to the completion items - items.push(createCompletionItem(terminalContext.commandLine, terminalContext.cursorPosition, prefix, specLabel)); + items.push(createCompletionItem(terminalContext.cursorPosition, prefix, specLabel)); } if (!terminalContext.commandLine.startsWith(specLabel)) { // the spec label is not the first word in the command line, so do not provide options or args @@ -283,7 +285,7 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon } for (const optionLabel of optionLabels) { if (!items.find(i => i.label === optionLabel) && optionLabel.startsWith(prefix) || (prefix.length > specLabel.length && prefix.trim() === specLabel)) { - items.push(createCompletionItem(terminalContext.commandLine, terminalContext.cursorPosition, prefix, optionLabel, option.description, vscode.TerminalCompletionItemKind.Flag)); + items.push(createCompletionItem(terminalContext.cursorPosition, prefix, optionLabel, option.description, vscode.TerminalCompletionItemKind.Flag)); } const expectedText = `${specLabel} ${optionLabel} `; if (!precedingText.includes(expectedText)) { @@ -331,7 +333,7 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon // Include builitin/available commands in the results for (const command of availableCommands) { if ((!terminalContext.commandLine.trim() || firstCommand && command.startsWith(firstCommand)) && !items.find(item => item.label === command)) { - items.push(createCompletionItem(terminalContext.commandLine, terminalContext.cursorPosition, prefix, command)); + items.push(createCompletionItem(terminalContext.cursorPosition, prefix, command)); } } } @@ -397,7 +399,7 @@ function getCompletionItemsFromArgs(args: Fig.SingleOrArray | undefined } if (suggestionLabel && suggestionLabel.startsWith(currentPrefix.trim())) { const description = typeof suggestion !== 'string' ? suggestion.description : ''; - items.push(createCompletionItem(terminalContext.commandLine, terminalContext.cursorPosition, wordBefore ?? '', suggestionLabel, description, vscode.TerminalCompletionItemKind.Argument)); + items.push(createCompletionItem(terminalContext.cursorPosition, wordBefore ?? '', suggestionLabel, description, vscode.TerminalCompletionItemKind.Argument)); } } } diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts index ea3b6cf16bf..ebbeae34b59 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalCompletionService.ts @@ -249,8 +249,8 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo kind, isDirectory, isFile: kind === TerminalCompletionItemKind.File, - replacementIndex: cursorPosition - lastWord.length > 0 ? cursorPosition - lastWord.length : cursorPosition, - replacementLength: lastWord.length > 0 ? lastWord.length : label.length + replacementIndex: cursorPosition - lastWord.length, + replacementLength: lastWord.length > 0 ? lastWord.length : cursorPosition }); } diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts index b3611df7444..3ce17efc700 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts @@ -69,6 +69,72 @@ suite('TerminalCompletionService', () => { }); suite('resolveResources should return folder completions', () => { + test('', async () => { + const resourceRequestConfig: TerminalResourceRequestConfig = { + cwd: URI.parse('file:///test'), + foldersRequested: true, + pathSeparator + }; + validResources = [URI.parse('file:///test')]; + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; + const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '', 1); + assert(!!result); + assert(result.length === 1); + assert.deepEqual(result![0], { + label: `.${pathSeparator}folder1${pathSeparator}`, + kind: TerminalCompletionItemKind.Folder, + isDirectory: true, + isFile: false, + replacementIndex: 1, + replacementLength: 1 + }); + }); + test('.', async () => { + const resourceRequestConfig: TerminalResourceRequestConfig = { + cwd: URI.parse('file:///test'), + foldersRequested: true, + pathSeparator + }; + validResources = [URI.parse('file:///test')]; + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; + const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '.', 2); + assert(!!result); + assert(result.length === 1); + assert.deepEqual(result![0], { + label: `.${pathSeparator}folder1${pathSeparator}`, + kind: TerminalCompletionItemKind.Folder, + isDirectory: true, + isFile: false, + replacementIndex: 1, + replacementLength: 1 + }); + }); + test('./', async () => { + const resourceRequestConfig: TerminalResourceRequestConfig = { + cwd: URI.parse('file:///test'), + foldersRequested: true, + pathSeparator + }; + validResources = [URI.parse('file:///test')]; + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; + const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './', 3); + assert(!!result); + assert(result.length === 1); + assert.deepEqual(result![0], { + label: `.${pathSeparator}folder1${pathSeparator}`, + kind: TerminalCompletionItemKind.Folder, + isDirectory: true, + isFile: false, + replacementIndex: 1, + replacementLength: 2 + }); + }); test('cd ', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), @@ -88,7 +154,7 @@ suite('TerminalCompletionService', () => { isDirectory: true, isFile: false, replacementIndex: 3, - replacementLength: 10 + replacementLength: 3 }); }); test('cd .', async () => { From 70409435e515cd7fc7951246690b31dbcd382326 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 16 Dec 2024 20:25:11 +0100 Subject: [PATCH 273/479] debt: remove migrations (#236239) * debt: remove migrations * remove migration test --- .../common/abstractSynchronizer.ts | 29 +------------ .../userDataSync/common/ignoredExtensions.ts | 8 ---- .../test/common/synchronizer.test.ts | 42 ------------------- 3 files changed, 1 insertion(+), 78 deletions(-) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 5f40daba148..dc3b9950ad7 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -593,10 +593,7 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa } async getLastSyncUserData(): Promise { - let storedLastSyncUserDataStateContent = this.getStoredLastSyncUserDataStateContent(); - if (!storedLastSyncUserDataStateContent) { - storedLastSyncUserDataStateContent = await this.migrateLastSyncUserData(); - } + const storedLastSyncUserDataStateContent = this.getStoredLastSyncUserDataStateContent(); // Last Sync Data state does not exist if (!storedLastSyncUserDataStateContent) { @@ -704,30 +701,6 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa await this.fileService.writeFile(this.lastSyncResource, VSBuffer.fromString(JSON.stringify(lastSyncRemoteUserData))); } - private async migrateLastSyncUserData(): Promise { - try { - const content = await this.fileService.readFile(this.lastSyncResource); - const userData = JSON.parse(content.value.toString()); - await this.fileService.del(this.lastSyncResource); - if (userData.ref && userData.content !== undefined) { - this.storageService.store(this.lastSyncUserDataStateKey, JSON.stringify({ - ...userData, - content: undefined, - }), StorageScope.APPLICATION, StorageTarget.MACHINE); - await this.writeLastSyncStoredRemoteUserData({ ref: userData.ref, syncData: userData.content === null ? null : JSON.parse(userData.content) }); - } else { - this.logService.info(`${this.syncResourceLogLabel}: Migrating last sync user data. Invalid data.`, userData); - } - } catch (error) { - if (error instanceof FileOperationError && error.fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { - this.logService.info(`${this.syncResourceLogLabel}: Migrating last sync user data. Resource does not exist.`); - } else { - this.logService.error(error); - } - } - return this.storageService.get(this.lastSyncUserDataStateKey, StorageScope.APPLICATION); - } - async getRemoteUserData(lastSyncData: IRemoteUserData | null): Promise { const { ref, content } = await this.getUserData(lastSyncData); let syncData: ISyncData | null = null; diff --git a/src/vs/platform/userDataSync/common/ignoredExtensions.ts b/src/vs/platform/userDataSync/common/ignoredExtensions.ts index 309cb745382..58e7f35f6ec 100644 --- a/src/vs/platform/userDataSync/common/ignoredExtensions.ts +++ b/src/vs/platform/userDataSync/common/ignoredExtensions.ts @@ -82,14 +82,6 @@ export class IgnoredExtensionsManagementService implements IIgnoredExtensionsMan } private getConfiguredIgnoredExtensions(): ReadonlyArray { - let userValue = this.configurationService.inspect('settingsSync.ignoredExtensions').userValue; - if (userValue !== undefined) { - return userValue; - } - userValue = this.configurationService.inspect('sync.ignoredExtensions').userValue; - if (userValue !== undefined) { - return userValue; - } return (this.configurationService.getValue('settingsSync.ignoredExtensions') || []).map(id => id.toLowerCase()); } } diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts index cbc65e458b1..4dfec270711 100644 --- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts +++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts @@ -1380,48 +1380,6 @@ suite('TestSynchronizer - Last Sync Data', () => { }); }); - test('last sync data is migrated', async () => { - await runWithFakedTimers({}, async () => { - const storageService = client.instantiationService.get(IStorageService); - const fileService = client.instantiationService.get(IFileService); - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - const machineId = await testObject.getMachineId(); - await fileService.writeFile(testObject.getLastSyncResource(), VSBuffer.fromString(JSON.stringify({ - ref: '1', - version: 1, - content: JSON.stringify({ - content: '0', - machineId, - version: 1 - }), - additionalData: { - foo: 'bar' - } - }))); - - const actual = await testObject.getLastSyncUserData(); - - assert.deepStrictEqual(storageService.get('settings.lastSyncUserData', StorageScope.APPLICATION), JSON.stringify({ - ref: '1', - version: 1, - additionalData: { - foo: 'bar' - } - })); - assert.deepStrictEqual(actual, { - ref: '1', - version: 1, - syncData: { - content: '0', - machineId, - version: 1 - }, - additionalData: { - foo: 'bar' - } - }); - }); - }); }); function assertConflicts(actual: IBaseResourcePreview[], expected: URI[]) { From a690eb691d5d1f015c10ca0f89b777c4d97d0094 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:28:00 +0100 Subject: [PATCH 274/479] Git - only show git blame information for the active text editor (#236274) --- extensions/git/src/blame.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index 9bb740fdeb2..b625439e15f 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -421,6 +421,8 @@ class GitBlameEditorDecoration { this._disposables.push(this._decorationType); workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); + window.onDidChangeActiveTextEditor(this._onDidChangeActiveTextEditor, this, this._disposables); + this._controller.onDidChangeBlameInformation(e => this._updateDecorations(e), this, this._disposables); } @@ -439,6 +441,18 @@ class GitBlameEditorDecoration { } } + private _onDidChangeActiveTextEditor(): void { + if (!this._getConfiguration().enabled) { + return; + } + + for (const editor of window.visibleTextEditors) { + if (editor !== window.activeTextEditor) { + editor.setDecorations(this._decorationType, []); + } + } + } + private _getConfiguration(): { enabled: boolean; template: string } { const config = workspace.getConfiguration('git'); const enabled = config.get('blame.editorDecoration.enabled', false); @@ -453,15 +467,6 @@ class GitBlameEditorDecoration { return; } - // Clear decorations for the other editors - for (const editor of window.visibleTextEditors) { - if (editor === textEditor) { - continue; - } - - editor.setDecorations(this._decorationType, []); - } - // Only support resources with `file` and `git` schemes if (textEditor.document.uri.scheme !== 'file' && !isGitUri(textEditor.document.uri)) { textEditor.setDecorations(this._decorationType, []); @@ -543,9 +548,7 @@ class GitBlameStatusBarItem { return; } - if (window.activeTextEditor) { - this._updateStatusBarItem(window.activeTextEditor); - } else { + if (!window.activeTextEditor) { this._statusBarItem?.hide(); } } From 24b8e3ce99ceccde43b8b19e8f15dc9d548d3c2d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:36:39 -0800 Subject: [PATCH 275/479] Remove workaround for swiftshader upstream issue Fixes #236148 --- .../terminal/browser/xterm/xtermTerminal.ts | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index fb5f0ddaf94..5ef6d497196 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -94,7 +94,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach private readonly _capabilities: ITerminalCapabilityStore; private static _suggestedRendererType: 'dom' | undefined = undefined; - private static _checkedWebglCompatible = false; private _attached?: { container: HTMLElement; options: IXtermAttachToElementOptions }; private _isPhysicalMouseWheel = MouseWheelClassifier.INSTANCE.isPhysicalMouseWheel(); @@ -667,24 +666,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach return; } - // Check if the the WebGL renderer is compatible with xterm.js: - // - https://github.com/microsoft/vscode/issues/190195 - // - https://github.com/xtermjs/xterm.js/issues/4665 - // - https://bugs.chromium.org/p/chromium/issues/detail?id=1476475 - if (!XtermTerminal._checkedWebglCompatible) { - XtermTerminal._checkedWebglCompatible = true; - const checkCanvas = document.createElement('canvas'); - const checkGl = checkCanvas.getContext('webgl2'); - const debugInfo = checkGl?.getExtension('WEBGL_debug_renderer_info'); - if (checkGl && debugInfo) { - const renderer = checkGl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); - if (renderer.startsWith('ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero)')) { - this._disableWebglForThisSession(); - return; - } - } - } - const Addon = await this._xtermAddonLoader.importAddon('webgl'); this._webglAddon = new Addon(); try { @@ -706,15 +687,11 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach // }, 5000); } catch (e) { this._logService.warn(`Webgl could not be loaded. Falling back to the DOM renderer`, e); - this._disableWebglForThisSession(); + XtermTerminal._suggestedRendererType = 'dom'; + this._disposeOfWebglRenderer(); } } - private _disableWebglForThisSession() { - XtermTerminal._suggestedRendererType = 'dom'; - this._disposeOfWebglRenderer(); - } - @debounce(100) private async _refreshLigaturesAddon(): Promise { if (!this.raw.element) { From e678c2e0c83c92fd6136234b4eb18453f7dcc89a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 Dec 2024 13:43:40 -0600 Subject: [PATCH 276/479] don't filter available commands in the extension (#236101) --- .../src/terminalSuggestMain.ts | 19 ++++++++----------- .../src/test/terminalSuggestMain.test.ts | 1 - 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 4eab00a8659..0c29d9688b1 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -251,7 +251,6 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon const items: vscode.TerminalCompletionItem[] = []; let filesRequested = false; let foldersRequested = false; - let specificSuggestionsProvided = false; const firstCommand = getFirstCommand(terminalContext.commandLine); for (const spec of specs) { const specLabels = getLabel(spec); @@ -297,7 +296,6 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon if (!argsCompletions) { continue; } - specificSuggestionsProvided = true; const argCompletions = argsCompletions.items; foldersRequested = foldersRequested || argsCompletions.foldersRequested; filesRequested = filesRequested || argsCompletions.filesRequested; @@ -305,7 +303,6 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon if (shellIntegrationCwd && (filesRequested || foldersRequested)) { cwd = await resolveCwdFromPrefix(prefix, shellIntegrationCwd) ?? shellIntegrationCwd; } - specificSuggestionsProvided = argsCompletions.specificSuggestionsProvided; return { items: argCompletions, filesRequested, foldersRequested, cwd }; } } @@ -322,17 +319,17 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon continue; } items.push(...argsCompletions.items); - specificSuggestionsProvided = argsCompletions.specificSuggestionsProvided; filesRequested = filesRequested || argsCompletions.filesRequested; foldersRequested = foldersRequested || argsCompletions.foldersRequested; } } } - - if (!specificSuggestionsProvided && (filesRequested === foldersRequested)) { + const shouldShowCommands = !terminalContext.commandLine.substring(0, terminalContext.cursorPosition).trimStart().includes(' '); + if (shouldShowCommands && (filesRequested === foldersRequested)) { // Include builitin/available commands in the results + const labels = new Set(items.map(i => i.label)); for (const command of availableCommands) { - if ((!terminalContext.commandLine.trim() || firstCommand && command.startsWith(firstCommand)) && !items.find(item => item.label === command)) { + if (!labels.has(command)) { items.push(createCompletionItem(terminalContext.cursorPosition, prefix, command)); } } @@ -361,7 +358,7 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon return { items, filesRequested, foldersRequested, cwd }; } -function getCompletionItemsFromArgs(args: Fig.SingleOrArray | undefined, currentPrefix: string, terminalContext: { commandLine: string; cursorPosition: number }): { items: vscode.TerminalCompletionItem[]; filesRequested: boolean; foldersRequested: boolean; specificSuggestionsProvided: boolean } | undefined { +function getCompletionItemsFromArgs(args: Fig.SingleOrArray | undefined, currentPrefix: string, terminalContext: { commandLine: string; cursorPosition: number }): { items: vscode.TerminalCompletionItem[]; filesRequested: boolean; foldersRequested: boolean } | undefined { if (!args) { return; } @@ -395,7 +392,7 @@ function getCompletionItemsFromArgs(args: Fig.SingleOrArray | undefined continue; } if (!arg.isVariadic && twoWordsBefore === suggestionLabel && wordBefore?.trim() === '') { - return { items: [], filesRequested, foldersRequested, specificSuggestionsProvided: false }; + return { items: [], filesRequested, foldersRequested }; } if (suggestionLabel && suggestionLabel.startsWith(currentPrefix.trim())) { const description = typeof suggestion !== 'string' ? suggestion.description : ''; @@ -404,11 +401,11 @@ function getCompletionItemsFromArgs(args: Fig.SingleOrArray | undefined } } if (items.length) { - return { items, filesRequested, foldersRequested, specificSuggestionsProvided: true }; + return { items, filesRequested, foldersRequested }; } } } - return { items, filesRequested, foldersRequested, specificSuggestionsProvided: false }; + return { items, filesRequested, foldersRequested }; } function osIsWindows(): boolean { diff --git a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts index c1979b251e2..70694606d99 100644 --- a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts +++ b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts @@ -56,7 +56,6 @@ function createCodeTestSpecs(executable: string): ITestSpec2[] { { input: `${executable} |`, expectedCompletions: codeOptions }, { input: `${executable} --locale |`, expectedCompletions: localeOptions }, { input: `${executable} --diff |`, expectedResourceRequests: { type: 'files', cwd: testCwd } }, - { input: `${executable} -di|`, expectedCompletions: codeOptions.filter(o => o.startsWith('di')), expectedResourceRequests: { type: 'both', cwd: testCwd } }, { input: `${executable} --diff ./file1 |`, expectedResourceRequests: { type: 'files', cwd: testCwd } }, { input: `${executable} --merge |`, expectedResourceRequests: { type: 'files', cwd: testCwd } }, { input: `${executable} --merge ./file1 ./file2 |`, expectedResourceRequests: { type: 'files', cwd: testCwd } }, From a09be5db237776197f9c0b1ef51a7043f256c7f3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 16 Dec 2024 20:46:57 +0100 Subject: [PATCH 277/479] Bug: setup hangs (fix microsoft/vscode-copilot#11361) (#236275) --- .../contrib/chat/browser/chatSetup.ts | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index e814b6acb21..54e081fbc0d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -574,7 +574,7 @@ class ChatSetupRequests extends Disposable { return this.resolveEntitlement(session, CancellationToken.None); } - async signUpLimited(session: AuthenticationSession): Promise { + async signUpLimited(session: AuthenticationSession): Promise { const body = { restricted_telemetry: this.telemetryService.telemetryLevel === TelemetryLevel.NONE ? 'disabled' : 'enabled', public_code_suggestions: 'enabled' @@ -583,7 +583,7 @@ class ChatSetupRequests extends Disposable { const response = await this.request(defaultChat.entitlementSignupLimitedUrl, 'POST', body, session, CancellationToken.None); if (!response) { this.onUnknownSignUpError('[chat setup] sign-up: no response'); - return false; + return undefined; } if (response.res.statusCode && response.res.statusCode !== 200) { @@ -594,7 +594,7 @@ class ChatSetupRequests extends Disposable { const responseError: { message: string } = JSON.parse(responseText); if (typeof responseError.message === 'string' && responseError.message) { this.onUnprocessableSignUpError(`[chat setup] sign-up: unprocessable entity (${responseError.message})`, responseError.message); - return false; + return undefined; } } } catch (error) { @@ -602,7 +602,7 @@ class ChatSetupRequests extends Disposable { } } this.onUnknownSignUpError(`[chat setup] sign-up: unexpected status code ${response.res.statusCode}`); - return false; + return undefined; } let responseText: string | null = null; @@ -614,7 +614,7 @@ class ChatSetupRequests extends Disposable { if (!responseText) { this.onUnknownSignUpError('[chat setup] sign-up: response has no content'); - return false; + return undefined; } let parsedResult: { subscribed: boolean } | undefined = undefined; @@ -623,20 +623,14 @@ class ChatSetupRequests extends Disposable { this.logService.trace(`[chat setup] sign-up: response is ${responseText}`); } catch (err) { this.onUnknownSignUpError(`[chat setup] sign-up: error parsing response (${err})`); + return undefined; } - const subscribed = Boolean(parsedResult?.subscribed); - if (subscribed) { - this.logService.trace('[chat setup] sign-up: successfully subscribed'); - } else { - this.logService.error('[chat setup] sign-up: not subscribed'); - } - - if (subscribed) { - this.update({ entitlement: ChatEntitlement.Limited }); - } + // We have made it this far, so the user either did sign-up or was signed-up already. + // That is, because the endpoint throws in all other case according to Patrick. + this.update({ entitlement: ChatEntitlement.Limited }); - return subscribed; + return Boolean(parsedResult?.subscribed); } private onUnknownSignUpError(logMessage: string): void { @@ -824,14 +818,14 @@ class ChatSetupController extends Disposable { let installResult: 'installed' | 'cancelled' | 'failedInstall' | undefined = undefined; const wasInstalled = this.context.state.installed; - let didSignUp = false; + let didSignUp: boolean | undefined = undefined; try { showCopilotView(this.viewsService, this.layoutService); if (entitlement !== ChatEntitlement.Limited && entitlement !== ChatEntitlement.Pro && entitlement !== ChatEntitlement.Unavailable) { didSignUp = await this.requests.signUpLimited(session); - if (!didSignUp) { + if (typeof didSignUp === 'undefined' /* error */) { this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedSignUp', signedIn }); } } From 58399f1f6840737cece0a8b6ad065d51422b4987 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:22:49 +0100 Subject: [PATCH 278/479] Git - truncate the git blame information after 50 characters (#236279) --- extensions/git/src/blame.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index b625439e15f..916d906a0a2 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -144,6 +144,8 @@ class GitBlameInformationCache { } export class GitBlameController { + private readonly _subjectMaxLength = 50; + private readonly _onDidChangeBlameInformation = new EventEmitter(); public readonly onDidChangeBlameInformation = this._onDidChangeBlameInformation.event; @@ -169,10 +171,14 @@ export class GitBlameController { } formatBlameInformationMessage(template: string, blameInformation: BlameInformation): string { + const subject = blameInformation.subject && blameInformation.subject.length > this._subjectMaxLength + ? `${blameInformation.subject.substring(0, this._subjectMaxLength)}\u2026` + : blameInformation.subject; + const templateTokens = { hash: blameInformation.hash, hashShort: blameInformation.hash.substring(0, 8), - subject: emojify(blameInformation.subject ?? ''), + subject: emojify(subject ?? ''), authorName: blameInformation.authorName ?? '', authorEmail: blameInformation.authorEmail ?? '', authorDate: new Date(blameInformation.authorDate ?? new Date()).toLocaleString(), From f1a2ce694a2899821233834096cf4acaf9ea81e2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 16 Dec 2024 21:52:59 +0100 Subject: [PATCH 279/479] remove rarely used reinstall extension command (#236278) --- .../abstractExtensionManagementService.ts | 1 - .../common/extensionManagement.ts | 1 - .../common/extensionManagementIpc.ts | 7 -- .../node/extensionManagementService.ts | 22 ------- .../browser/extensions.contribution.ts | 13 +--- .../extensions/browser/extensionsActions.ts | 66 ------------------- .../browser/extensionsWorkbenchService.ts | 11 ---- .../contrib/extensions/common/extensions.ts | 1 - .../common/extensionManagementService.ts | 9 --- .../common/webExtensionManagementService.ts | 1 - .../test/browser/workbenchTestServices.ts | 3 - 11 files changed, 1 insertion(+), 134 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 59f359d659c..6b854b0bd87 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -111,7 +111,6 @@ export abstract class CommontExtensionManagementService extends Disposable imple abstract getInstalled(type?: ExtensionType, profileLocation?: URI, productVersion?: IProductVersion): Promise; abstract copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise; abstract download(extension: IGalleryExtension, operation: InstallOperation, donotVerifySignature: boolean): Promise; - abstract reinstallFromGallery(extension: ILocalExtension): Promise; abstract cleanUp(): Promise; abstract updateMetadata(local: ILocalExtension, metadata: Partial, profileLocation: URI): Promise; } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index ba8a8e86fee..155079831fc 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -572,7 +572,6 @@ export interface IExtensionManagementService { uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise; uninstallExtensions(extensions: UninstallExtensionInfo[]): Promise; toggleAppliationScope(extension: ILocalExtension, fromProfileLocation: URI): Promise; - reinstallFromGallery(extension: ILocalExtension): Promise; getInstalled(type?: ExtensionType, profileLocation?: URI, productVersion?: IProductVersion): Promise; getExtensionsControlManifest(): Promise; copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index d9542cb5e6a..e20ce088e42 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -144,9 +144,6 @@ export class ExtensionManagementChannel implements IServerChannel { const arg: UninstallExtensionInfo[] = args[0]; return this.service.uninstallExtensions(arg.map(({ extension, options }) => ({ extension: transformIncomingExtension(extension, uriTransformer), options: transformIncomingOptions(options, uriTransformer) }))); } - case 'reinstallFromGallery': { - return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer)); - } case 'getInstalled': { const extensions = await this.service.getInstalled(args[0], transformIncomingURI(args[1], uriTransformer), args[2]); return extensions.map(e => transformOutgoingExtension(e, uriTransformer)); @@ -299,10 +296,6 @@ export class ExtensionManagementChannelClient extends CommontExtensionManagement } - reinstallFromGallery(extension: ILocalExtension): Promise { - return Promise.resolve(this.channel.call('reinstallFromGallery', [extension])).then(local => transformIncomingExtension(local, null)); - } - getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI, productVersion?: IProductVersion): Promise { return Promise.resolve(this.channel.call('getInstalled', [type, extensionsProfileResource, productVersion])) .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index a55978c612f..92405eefb75 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -8,7 +8,6 @@ import { Promises, Queue } from '../../../base/common/async.js'; import { VSBuffer } from '../../../base/common/buffer.js'; import { CancellationToken } from '../../../base/common/cancellation.js'; import { IStringDictionary } from '../../../base/common/collections.js'; -import { toErrorMessage } from '../../../base/common/errorMessage.js'; import { CancellationError, getErrorMessage } from '../../../base/common/errors.js'; import { Emitter } from '../../../base/common/event.js'; import { hash } from '../../../base/common/hash.js'; @@ -215,27 +214,6 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return local; } - async reinstallFromGallery(extension: ILocalExtension): Promise { - this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.identifier.id); - if (!this.galleryService.isEnabled()) { - throw new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")); - } - - const targetPlatform = await this.getTargetPlatform(); - const [galleryExtension] = await this.galleryService.getExtensions([{ ...extension.identifier, preRelease: extension.preRelease }], { targetPlatform, compatible: true }, CancellationToken.None); - if (!galleryExtension) { - throw new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled")); - } - - await this.extensionsScanner.setUninstalled(extension); - try { - await this.extensionsScanner.removeUninstalledExtension(extension); - } catch (e) { - throw new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))); - } - return this.installFromGallery(galleryExtension); - } - protected copyExtension(extension: ILocalExtension, fromProfileLocation: URI, toProfileLocation: URI, metadata: Partial): Promise { return this.extensionsScanner.copyExtension(extension, fromProfileLocation, toProfileLocation, metadata); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 4a2b68d2f91..4a55c93c25c 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -14,7 +14,7 @@ import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsServi import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from '../../../common/contributions.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, extensionsSearchActionsMenu, UPDATE_ACTIONS_GROUP, IExtensionArg, ExtensionRuntimeActionType, EXTENSIONS_CATEGORY } from '../common/extensions.js'; -import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction, InstallAnotherVersionAction, InstallAction } from './extensionsActions.js'; +import { InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction, InstallAnotherVersionAction, InstallAction } from './extensionsActions.js'; import { ExtensionsInput } from '../common/extensionsInput.js'; import { ExtensionEditor } from './extensionEditor.js'; import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, BuiltInExtensionsContext, SearchMarketplaceExtensionsContext, RecommendedExtensionsContext, DefaultViewsContext, ExtensionsSortByContext, SearchHasTextContext, ExtensionsSearchValueContext } from './extensionsViewlet.js'; @@ -1292,17 +1292,6 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }, run: () => runAction(this.instantiationService.createInstance(InstallSpecificVersionOfExtensionAction, InstallSpecificVersionOfExtensionAction.ID, InstallSpecificVersionOfExtensionAction.LABEL)) }); - - this.registerExtensionAction({ - id: ReinstallAction.ID, - title: { value: ReinstallAction.LABEL, original: 'Reinstall Extension...' }, - category: Categories.Developer, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)) - }, - run: () => runAction(this.instantiationService.createInstance(ReinstallAction, ReinstallAction.ID, ReinstallAction.LABEL)) - }); } // Extension Context Menu diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index a932e4f9ab5..052e14050eb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2843,72 +2843,6 @@ export class ExtensionStatusAction extends ExtensionAction { } } -export class ReinstallAction extends Action { - - static readonly ID = 'workbench.extensions.action.reinstall'; - static readonly LABEL = localize('reinstall', "Reinstall Extension..."); - - constructor( - id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL, - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @INotificationService private readonly notificationService: INotificationService, - @IHostService private readonly hostService: IHostService, - @IExtensionService private readonly extensionService: IExtensionService - ) { - super(id, label); - } - - override get enabled(): boolean { - return this.extensionsWorkbenchService.local.filter(l => !l.isBuiltin && l.local).length > 0; - } - - override run(): Promise { - return this.quickInputService.pick(this.getEntries(), { placeHolder: localize('selectExtensionToReinstall', "Select Extension to Reinstall") }) - .then(pick => pick && this.reinstallExtension(pick.extension)); - } - - private getEntries(): Promise<(IQuickPickItem & { extension: IExtension })[]> { - return this.extensionsWorkbenchService.queryLocal() - .then(local => { - const entries = local - .filter(extension => !extension.isBuiltin && extension.server !== this.extensionManagementServerService.webExtensionManagementServer) - .map(extension => { - return { - id: extension.identifier.id, - label: extension.displayName, - description: extension.identifier.id, - extension, - }; - }); - return entries; - }); - } - - private reinstallExtension(extension: IExtension): Promise { - return this.extensionsWorkbenchService.openSearch('@installed ') - .then(() => { - return this.extensionsWorkbenchService.reinstall(extension) - .then(extension => { - const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local))); - const message = requireReload ? localize('ReinstallAction.successReload', "Please reload Visual Studio Code to complete reinstalling the extension {0}.", extension.identifier.id) - : localize('ReinstallAction.success', "Reinstalling the extension {0} is completed.", extension.identifier.id); - const actions = requireReload ? [{ - label: localize('InstallVSIXAction.reloadNow', "Reload Now"), - run: () => this.hostService.reload() - }] : []; - this.notificationService.prompt( - Severity.Info, - message, - actions, - { sticky: true } - ); - }, error => this.notificationService.error(error)); - }); - } -} - export class InstallSpecificVersionOfExtensionAction extends Action { static readonly ID = 'workbench.extensions.action.install.specificVersion'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 1a862498873..1a28f0027ee 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -2618,17 +2618,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension extension.displayName, dependents[0].displayName, dependents[1].displayName); } - reinstall(extension: IExtension): Promise { - return this.doInstall(extension, () => { - const ext = extension.local ? extension : this.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; - const toReinstall: ILocalExtension | null = ext && ext.local ? ext.local : null; - if (!toReinstall) { - throw new Error('Missing local'); - } - return this.extensionManagementService.reinstallFromGallery(toReinstall); - }); - } - isExtensionIgnoredToSync(extension: IExtension): boolean { return extension.local ? !this.isInstalledExtensionSynced(extension.local) : this.extensionsSyncManagementService.hasToNeverSyncExtension(extension.identifier.id); diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 916628ddc60..4cd9d5ea1d4 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -143,7 +143,6 @@ export interface IExtensionsWorkbenchService { installInServer(extension: IExtension, server: IExtensionManagementServer): Promise; downloadVSIX(extension: string, prerelease: boolean): Promise; uninstall(extension: IExtension): Promise; - reinstall(extension: IExtension): Promise; togglePreRelease(extension: IExtension): Promise; canSetLanguage(extension: IExtension): boolean; setLanguage(extension: IExtension): Promise; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 6312b05fe32..d8236828324 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -256,15 +256,6 @@ export class ExtensionManagementService extends Disposable implements IWorkbench } - async reinstallFromGallery(extension: ILocalExtension): Promise { - const server = this.getServer(extension); - if (server) { - await this.checkForWorkspaceTrust(extension.manifest, false); - return server.extensionManagementService.reinstallFromGallery(extension); - } - return Promise.reject(`Invalid location ${extension.location.toString()}`); - } - updateMetadata(extension: ILocalExtension, metadata: Partial): Promise { const server = this.getServer(extension); if (server) { diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 30971801a82..65b49fb7bd1 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -201,7 +201,6 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe zip(extension: ILocalExtension): Promise { throw new Error('unsupported'); } getManifest(vsix: URI): Promise { throw new Error('unsupported'); } download(): Promise { throw new Error('unsupported'); } - reinstallFromGallery(): Promise { throw new Error('unsupported'); } async cleanUp(): Promise { } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 4a850d040d8..a914f4586d4 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -2226,9 +2226,6 @@ export class TestWorkbenchExtensionManagementService implements IWorkbenchExtens uninstallExtensions(extensions: UninstallExtensionInfo[]): Promise { throw new Error('Method not implemented.'); } - async reinstallFromGallery(extension: ILocalExtension): Promise { - throw new Error('Method not implemented.'); - } async getInstalled(type?: ExtensionType | undefined): Promise { return []; } getExtensionsControlManifest(): Promise { throw new Error('Method not implemented.'); From 40582112ab4e36a7e01493973f6f2d25148b6cf2 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:53:25 +0100 Subject: [PATCH 280/479] SCM - more quick diff cleanup (#236280) --- .../contrib/scm/browser/quickDiffModel.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index 9907a627c27..3d1ec39c3f6 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -33,8 +33,14 @@ export const IQuickDiffModelService = createDecorator('I export interface QuickDiffModelOptions { readonly algorithm: DiffAlgorithmName; + readonly maxComputationTimeMs?: number; } +const decoratorQuickDiffModelOptions: QuickDiffModelOptions = { + algorithm: 'legacy', + maxComputationTimeMs: 1000 +}; + export interface IQuickDiffModelService { _serviceBrand: undefined; @@ -52,7 +58,7 @@ class QuickDiffModelReferenceCollection extends ReferenceCollection | undefined { + createQuickDiffModelReference(resource: URI, options: QuickDiffModelOptions = decoratorQuickDiffModelOptions): IReference | undefined { const textFileModel = this.textFileService.files.get(resource); if (!textFileModel?.isResolved()) { return undefined; } - resource = options === undefined - ? this.uriIdentityService.asCanonicalUri(resource) - : this.uriIdentityService.asCanonicalUri(resource).with({ query: JSON.stringify(options) }); - + resource = this.uriIdentityService.asCanonicalUri(resource).with({ query: JSON.stringify(options) }); return this._references.acquire(resource.toString(), textFileModel, options); } } @@ -119,7 +122,7 @@ export class QuickDiffModel extends Disposable { constructor( textFileModel: IResolvedTextFileEditorModel, - private readonly options: QuickDiffModelOptions | undefined, + private readonly options: QuickDiffModelOptions, @ISCMService private readonly scmService: ISCMService, @IQuickDiffService private readonly quickDiffService: IQuickDiffService, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, @@ -273,14 +276,11 @@ export class QuickDiffModel extends Disposable { } private async _diff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<{ changes: readonly IChange[] | null; changes2: readonly LineRangeMapping[] | null }> { - // When no algorithm is specified, we use the legacy diff algorithm along with a 1000ms - // timeout as the diff information is being used for the quick diff editor decorations - const algorithm = this.options?.algorithm ?? 'legacy'; - const maxComputationTimeMs = this.options?.algorithm ? Number.MAX_SAFE_INTEGER : 1000; + const maxComputationTimeMs = this.options.maxComputationTimeMs ?? Number.MAX_SAFE_INTEGER; const result = await this.editorWorkerService.computeDiff(original, modified, { computeMoves: false, ignoreTrimWhitespace, maxComputationTimeMs - }, algorithm); + }, this.options.algorithm); return { changes: result ? toLineChanges(DiffState.fromDiffResult(result)) : null, changes2: result?.changes ?? null }; } @@ -361,7 +361,7 @@ export class QuickDiffModel extends Disposable { // When the QuickDiffModel is created for a diff editor, there is no // need to compute the diff information for the `isSCM` quick diff // provider as that information will be provided by the diff editor - return this.options?.algorithm !== undefined + return this.options.maxComputationTimeMs === undefined ? quickDiffs.filter(quickDiff => !quickDiff.isSCM) : quickDiffs; } From 057dd7b933f684d018a614a1ce03e4db5c3614c9 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 Dec 2024 15:31:45 -0600 Subject: [PATCH 281/479] get `No suggestions` to show up when triggered for terminal suggest (#236264) --- .../suggest/browser/terminalSuggestAddon.ts | 3 +-- .../suggest/browser/simpleSuggestWidget.ts | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index 81476106cf2..331d36a2e58 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -42,7 +42,6 @@ export interface ISuggestController { acceptSelectedSuggestion(suggestion?: Pick): void; hideSuggestWidget(): void; } - export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggestController { private _terminal?: Terminal; @@ -366,7 +365,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest } const suggestWidget = this._ensureSuggestWidget(this._terminal); suggestWidget.setCompletionModel(model); - if (model.items.length === 0 || !this._promptInputModel) { + if (!this._promptInputModel) { return; } this._model = model; diff --git a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts index 6f22b0d0d77..61fb7437cea 100644 --- a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts +++ b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts @@ -59,6 +59,9 @@ export interface IWorkbenchSuggestWidgetOptions { export class SimpleSuggestWidget extends Disposable { + private static LOADING_MESSAGE: string = localize('suggestWidget.loading', "Loading..."); + private static NO_SUGGESTIONS_MESSAGE: string = localize('suggestWidget.noSuggestions', "No suggestions."); + private _state: State = State.Hidden; private _completionModel?: SimpleCompletionModel; private _cappedHeight?: { wanted: number; capped: number }; @@ -67,6 +70,7 @@ export class SimpleSuggestWidget extends Disposable { private readonly _pendingLayout = this._register(new MutableDisposable()); readonly element: ResizableHTMLElement; + private readonly _messageElement: HTMLElement; private readonly _listElement: HTMLElement; private readonly _list: List; private readonly _status?: SuggestWidgetStatus; @@ -193,6 +197,8 @@ export class SimpleSuggestWidget extends Disposable { } })); + this._messageElement = dom.append(this.element.domNode, dom.$('.message')); + if (options.statusBarMenuId) { this._status = this._register(instantiationService.createInstance(SuggestWidgetStatus, this.element.domNode, options.statusBarMenuId)); this.element.domNode.classList.toggle('with-status-bar', true); @@ -287,7 +293,9 @@ export class SimpleSuggestWidget extends Disposable { switch (state) { case State.Hidden: - // dom.hide(this._messageElement, this._listElement, this._status.element); + if (this._status) { + dom.hide(this._messageElement, this._listElement, this._status.element); + } dom.hide(this._listElement); if (this._status) { dom.hide(this._status?.element); @@ -307,30 +315,30 @@ export class SimpleSuggestWidget extends Disposable { break; case State.Loading: this.element.domNode.classList.add('message'); - // this._messageElement.textContent = SuggestWidget.LOADING_MESSAGE; + this._messageElement.textContent = SimpleSuggestWidget.LOADING_MESSAGE; dom.hide(this._listElement); if (this._status) { dom.hide(this._status?.element); } - // dom.show(this._messageElement); + dom.show(this._messageElement); // this._details.hide(); this._show(); // this._focusedItem = undefined; break; case State.Empty: this.element.domNode.classList.add('message'); - // this._messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE; + this._messageElement.textContent = SimpleSuggestWidget.NO_SUGGESTIONS_MESSAGE; dom.hide(this._listElement); if (this._status) { dom.hide(this._status?.element); } - // dom.show(this._messageElement); + dom.show(this._messageElement); // this._details.hide(); this._show(); // this._focusedItem = undefined; break; case State.Open: - // dom.hide(this._messageElement); + dom.hide(this._messageElement); dom.show(this._listElement); if (this._status) { dom.show(this._status?.element); @@ -338,7 +346,7 @@ export class SimpleSuggestWidget extends Disposable { this._show(); break; case State.Frozen: - // dom.hide(this._messageElement); + dom.hide(this._messageElement); dom.show(this._listElement); if (this._status) { dom.show(this._status?.element); @@ -346,7 +354,7 @@ export class SimpleSuggestWidget extends Disposable { this._show(); break; case State.Details: - // dom.hide(this._messageElement); + dom.hide(this._messageElement); dom.show(this._listElement); if (this._status) { dom.show(this._status?.element); From d0ecdc1f5551d21f3d7822c8adc026ad4a9bf7e3 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 Dec 2024 16:38:14 -0600 Subject: [PATCH 282/479] get terminal editor's name to persist upon rename cancellation (#236281) fix #235931 --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 45d04fac451..9218eff9f83 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1724,7 +1724,9 @@ async function renameWithQuickPick(c: ITerminalServicesCollection, accessor: Ser value: instance.title, prompt: localize('workbench.action.terminal.rename.prompt', "Enter terminal name"), }); - instance.rename(title); + if (title) { + instance.rename(title); + } } } From 8264792d81f87d1524f4c26b07e571a28d3fc24a Mon Sep 17 00:00:00 2001 From: Oleg Solomko Date: Mon, 16 Dec 2024 15:07:15 -0800 Subject: [PATCH 283/479] add prompt snippets support (#234220) * [prompt snippets]: working on recursive file references * [prompt snippets]: working on file contents codec * [prompt snippets]: implement the `LinesCodecDecoder` * [prompt snippets]: working on the `SimpleTokensCodecDecoder` * [prompt snippets]: implement first version of `PromptSyntaxCodec` * [prompt snippets]: add `Range` attributes to all the tokens * [prompt snippets]: refactoring components to standalone files * [prompt snippets]: move out components to separate files, working on the first unit test * [prompt snippets]: add the first unit test for `LinesDecoder` * [prompt snippets]: add first unit test for the `SimpleDecoder` * [prompt snippets]: refactor `SimpleDecoder` logic * [prompt snippets]: make `SimpleDecoder` handle `\t` characters and improve unit tests * [prompt snippets]: add unit tests for the `ChatbotPromptCodec` codec * [prompt snippets]: add unit test for the `ChatbotPromptReference` * [prompt snippets]: working on enhancing prompt context * [prompt snippets]: enahnce working set file references on submit * [prompt snippets]: cleanup * [prompt snippets]: implement automatic references resolution on file variable addition * [prompt snippets]: implement prompt snippets for implicit context files * [prompt snippets]: improve implicit context file rendering * [prompt snippets]: remove redundant `ChatReference` class * [prompt snippets]: `PromptReference` can now subscribe to filesystem changes, added unit test for the `assertDefined` util * [prompt snippets]: make `LinesDecoder` to also emit `NewLine` tokens, add more unit tests for it * [prompt snippets]: resolve nested references for dynamic variables and update variable label * [prompt snippets]: add nested file references to implicit context of prompt request * [prompt snippets]: variable hover label now includes nested file references * [prompt snippets]: add the `TestDecoder` and `TestLinesDecoder` reusable test utilities * [prompt snippets]: add unit tests for `ChatbotPromptDecoder` decoder * [prompt snippets]: refactor decoders to eliminate static type hacks * [prompt snippets]: improve `BaseDecoder` logic and docs * [prompt snippets]: add unit tests for the new reduce-less logic of the stream * [prompt snippets]: improve doc comments, finish the basic unit test for the `PromptFileReference` class * [prompt snippets]: finish the main unit test for the `PromptFileReference` class * [prompt snippets]: make the `PromptFileReference` class ininite-reference-recursion-proof and add approptiate tests * [prompt snippets]: cleanup * [prompt snippets]: ensure codecs work well with UTF16 codepoints * [prompt snippets]: add `randomInt()` utility and related tests * [prompt snippets]: refactor and add more unit tests * [prompt snippets]: fix unresolved `Buffer` import reference * [prompt snippets]: refactor `LinesDecoder` to use `VSBuffer` as underlying data source * [prompt snippets]: make the `ImplicitContext` to create prompt file reference object only if `prompt-snippets` config value is set * [prompt snippets]: make the `ChatDynamicVariable` to resolve prompt file reference object only if `prompt-snippets` config value is set * [prompt snippets]: localize (+N more) labels * [prompt snippets]: pre PR cleanups * [prompt snippets]: minor cleanups * [prompt snippets]: address some PR feedback, fix an unit test issue * [prompt snippets]: move file to common layers * [prompt snippets]: move base codec types to base/common * [prompt snippets]: move more codec files around * [prompt snippets]: update a code comment * [prompt snippets]: fix `assert.throws()` API incompatibility issue * [prompt snippets]: minor cleanup * [prompt snippets]: improve unit tests for the `PromptFileReference` * [prompt snippets]: skip unit tests of `PromptFileReference` on Windows for now * [prompt snippets]: address PR feedback * [prompt snippets]: add `PromptFileReference.isPromptSnippetFile` getter * [prompt snippets]: move the `assertDefined` utility to `types.ts` * use service injection in `PromptFileReference` class * [prompt snippets]: remove formatting changes * [prompt snippets]: remove more formatting changes * [prompt snippets]: revert more formatting changes * [prompt snippets]: add logic to dispose existing variables in `ChatDynamicVariableModel` * [prompt snippets]: cleanup * [prompt snippets]: minor cleanup * [prompt snippets]: remove redundant disposable registrations, cleanup more formatting changes * [prompt snippets]: improve cross-platform newline handling, add `vertical tab` and `form feed` tokens * [prompt snippets]: minor types naming cleanup * [prompt snippets]: make `ChatPromptCodec` to be a global singleton object and improve doc comments * [prompt snippets]: address PR feedback * [prompt snippets]: remove (+N more) label, ensure that `ChatFileReferences` are scoped to files only * [prompt snippets]: revert changes for the `ImplicitContext` * [prompt snippets]: cleanup changes in `ChatDynamicVariableModel` * [prompt snippets]: revert changes in the `ChatRequestParser` * [prompt snippets]: improve history navigation * [prompt snippets]: address PR feedback * [prompt snippets]: allow non-prompt-snippet files to be referenced, but don't parse their contents --- src/vs/base/common/codecs/asyncDecoder.ts | 85 ++++ src/vs/base/common/codecs/baseDecoder.ts | 322 ++++++++++++ src/vs/base/common/codecs/types/ICodec.d.ts | 22 + src/vs/base/common/numbers.ts | 62 +++ src/vs/base/common/stream.ts | 36 +- src/vs/base/common/types.ts | 47 +- src/vs/base/test/common/numbers.test.ts | 177 ++++++- src/vs/base/test/common/stream.test.ts | 100 +++- src/vs/base/test/common/types.test.ts | 130 +++++ src/vs/editor/common/codecs/baseToken.ts | 57 +++ .../common/codecs/linesCodec/linesDecoder.ts | 213 ++++++++ .../linesCodec/tokens/carriageReturn.ts | 59 +++ .../common/codecs/linesCodec/tokens/line.ts | 63 +++ .../codecs/linesCodec/tokens/newLine.ts | 58 +++ .../codecs/simpleCodec/simpleDecoder.ts | 100 ++++ .../codecs/simpleCodec/tokens/formFeed.ts | 46 ++ .../common/codecs/simpleCodec/tokens/space.ts | 46 ++ .../common/codecs/simpleCodec/tokens/tab.ts | 47 ++ .../codecs/simpleCodec/tokens/verticalTab.ts | 46 ++ .../common/codecs/simpleCodec/tokens/word.ts | 72 +++ .../test/common/codecs/linesDecoder.test.ts | 140 ++++++ .../test/common/codecs/simpleDecoder.test.ts | 111 ++++ .../editor/test/common/utils/testDecoder.ts | 115 +++++ .../contrib/chat/browser/chatInputPart.ts | 25 +- .../contrib/chat/browser/chatWidget.ts | 2 +- .../browser/contrib/chatDynamicVariables.ts | 96 +++- .../chatDynamicVariables/chatFileReference.ts | 79 +++ .../codecs/chatPromptCodec/chatPromptCodec.ts | 52 ++ .../chatPromptCodec/chatPromptDecoder.ts | 45 ++ .../chatPromptCodec/tokens/fileReference.ts | 98 ++++ .../chat/common/promptFileReference.ts | 414 +++++++++++++++ .../chat/common/promptFileReferenceErrors.ts | 129 +++++ .../common/codecs/chatPromptCodec.test.ts | 77 +++ .../common/codecs/chatPromptDecoder.test.ts | 75 +++ .../test/common/promptFileReference.test.ts | 474 ++++++++++++++++++ 35 files changed, 3687 insertions(+), 33 deletions(-) create mode 100644 src/vs/base/common/codecs/asyncDecoder.ts create mode 100644 src/vs/base/common/codecs/baseDecoder.ts create mode 100644 src/vs/base/common/codecs/types/ICodec.d.ts create mode 100644 src/vs/editor/common/codecs/baseToken.ts create mode 100644 src/vs/editor/common/codecs/linesCodec/linesDecoder.ts create mode 100644 src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts create mode 100644 src/vs/editor/common/codecs/linesCodec/tokens/line.ts create mode 100644 src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/space.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts create mode 100644 src/vs/editor/common/codecs/simpleCodec/tokens/word.ts create mode 100644 src/vs/editor/test/common/codecs/linesDecoder.test.ts create mode 100644 src/vs/editor/test/common/codecs/simpleDecoder.test.ts create mode 100644 src/vs/editor/test/common/utils/testDecoder.ts create mode 100644 src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts create mode 100644 src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptCodec.ts create mode 100644 src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptDecoder.ts create mode 100644 src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/tokens/fileReference.ts create mode 100644 src/vs/workbench/contrib/chat/common/promptFileReference.ts create mode 100644 src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts create mode 100644 src/vs/workbench/contrib/chat/test/common/codecs/chatPromptCodec.test.ts create mode 100644 src/vs/workbench/contrib/chat/test/common/codecs/chatPromptDecoder.test.ts create mode 100644 src/vs/workbench/contrib/chat/test/common/promptFileReference.test.ts diff --git a/src/vs/base/common/codecs/asyncDecoder.ts b/src/vs/base/common/codecs/asyncDecoder.ts new file mode 100644 index 00000000000..efbcf9a7fe4 --- /dev/null +++ b/src/vs/base/common/codecs/asyncDecoder.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../lifecycle.js'; +import { BaseDecoder } from './baseDecoder.js'; + +/** + * Asynchronous interator wrapper for a decoder. + */ +export class AsyncDecoder, K extends NonNullable = NonNullable> extends Disposable { + // Buffer of messages that have been decoded but not yet consumed. + private readonly messages: T[] = []; + + /** + * A transient promise that is resolved when a new event + * is received. Used in the situation when there is no new + * data avaialble and decoder stream did not finish yet, + * hence we need to wait until new event is received. + */ + private resolveOnNewEvent?: (value: void) => void; + + /** + * @param decoder The decoder instance to wrap. + * + * Note! Assumes ownership of the `decoder` object, hence will `dipose` + * it when the decoder stream is ended. + */ + constructor( + private readonly decoder: BaseDecoder, + ) { + super(); + + this._register(decoder); + } + + /** + * Async iterator implementation. + */ + async *[Symbol.asyncIterator](): AsyncIterator { + // callback is called when `data` or `end` event is received + const callback = (data?: T) => { + if (data !== undefined) { + this.messages.push(data); + } else { + this.decoder.removeListener('data', callback); + this.decoder.removeListener('end', callback); + } + + // is the promise resolve callback is present, + // then call it and remove the reference + if (this.resolveOnNewEvent) { + this.resolveOnNewEvent(); + delete this.resolveOnNewEvent; + } + }; + this.decoder.on('data', callback); + this.decoder.on('end', callback); + + // start flowing the decoder stream + this.decoder.start(); + + while (true) { + const maybeMessage = this.messages.shift(); + if (maybeMessage !== undefined) { + yield maybeMessage; + continue; + } + + // if no data available and stream ended, we're done + if (this.decoder.isEnded) { + this.dispose(); + + return null; + } + + // stream isn't ended so wait for the new + // `data` or `end` event to be received + await new Promise((resolve) => { + this.resolveOnNewEvent = resolve; + }); + } + } +} diff --git a/src/vs/base/common/codecs/baseDecoder.ts b/src/vs/base/common/codecs/baseDecoder.ts new file mode 100644 index 00000000000..5404e3df0bf --- /dev/null +++ b/src/vs/base/common/codecs/baseDecoder.ts @@ -0,0 +1,322 @@ +/*--------------------------------------------------------------------------------------------- + * 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.js'; +import { Emitter } from '../event.js'; +import { ReadableStream } from '../stream.js'; +import { AsyncDecoder } from './asyncDecoder.js'; +import { Disposable, IDisposable } from '../lifecycle.js'; + +/** + * Event names of {@link ReadableStream} stream. + */ +export type TStreamListenerNames = 'data' | 'error' | 'end'; + +/** + * Base decoder class that can be used to convert stream messages data type + * from one type to another. For instance, a stream of binary data can be + * "decoded" into a stream of well defined objects. + * Intended to be a part of "codec" implementation rather than used directly. + */ +export abstract class BaseDecoder< + T extends NonNullable, + K extends NonNullable = NonNullable, +> extends Disposable implements ReadableStream { + /** + * Flag that indicates if the decoder stream has ended. + */ + protected ended = false; + + protected readonly _onData = this._register(new Emitter()); + protected readonly _onEnd = this._register(new Emitter()); + protected readonly _onError = this._register(new Emitter()); + + /** + * A store of currently registered event listeners. + */ + private readonly _listeners: Map> = new Map(); + + /** + * @param stream The input stream to decode. + */ + constructor( + protected readonly stream: ReadableStream, + ) { + super(); + + this.tryOnStreamData = this.tryOnStreamData.bind(this); + this.onStreamError = this.onStreamError.bind(this); + this.onStreamEnd = this.onStreamEnd.bind(this); + } + + /** + * This method is called when a new incomming data + * is received from the input stream. + */ + protected abstract onStreamData(data: K): void; + + /** + * Start receiveing data from the stream. + * @throws if the decoder stream has already ended. + */ + public start(): this { + assert( + !this.ended, + 'Cannot start stream that has already ended.', + ); + + this.stream.on('data', this.tryOnStreamData); + this.stream.on('error', this.onStreamError); + this.stream.on('end', this.onStreamEnd); + + // this allows to compose decoders together, - if a decoder + // instance is passed as a readble stream to this decoder, + // then we need to call `start` on it too + if (this.stream instanceof BaseDecoder) { + this.stream.start(); + } + + return this; + } + + /** + * Check if the decoder has been ended hence has + * no more data to produce. + */ + public get isEnded(): boolean { + return this.ended; + } + + /** + * Automatically catch and dispatch errors thrown inside `onStreamData`. + */ + private tryOnStreamData(data: K): void { + try { + this.onStreamData(data); + } catch (error) { + this.onStreamError(error); + } + } + + 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: TStreamListenerNames, callback: unknown): void { + if (event === 'data') { + return this.onData(callback as (data: T) => void); + } + + if (event === 'error') { + return this.onError(callback as (error: Error) => void); + } + + if (event === 'end') { + return this.onEnd(callback as () => void); + } + + throw new Error(`Invalid event name: ${event}`); + } + + /** + * Add listener for the `data` event. + * @throws if the decoder stream has already ended. + */ + public onData(callback: (data: T) => void): void { + assert( + !this.ended, + 'Cannot subscribe to the `data` event because the decoder stream has already ended.', + ); + + let currentListeners = this._listeners.get('data'); + + if (!currentListeners) { + currentListeners = new Map(); + this._listeners.set('data', currentListeners); + } + + currentListeners.set(callback, this._onData.event(callback)); + } + + /** + * Add listener for the `error` event. + * @throws if the decoder stream has already ended. + */ + public onError(callback: (error: Error) => void): void { + assert( + !this.ended, + 'Cannot subscribe to the `error` event because the decoder stream has already ended.', + ); + + let currentListeners = this._listeners.get('error'); + + if (!currentListeners) { + currentListeners = new Map(); + this._listeners.set('error', currentListeners); + } + + currentListeners.set(callback, this._onError.event(callback)); + } + + /** + * Add listener for the `end` event. + * @throws if the decoder stream has already ended. + */ + public onEnd(callback: () => void): void { + assert( + !this.ended, + 'Cannot subscribe to the `end` event because the decoder stream has already ended.', + ); + + let currentListeners = this._listeners.get('end'); + + if (!currentListeners) { + currentListeners = new Map(); + this._listeners.set('end', currentListeners); + } + + currentListeners.set(callback, this._onEnd.event(callback)); + } + + /** + * Remove all existing event listeners. + */ + public removeAllListeners(): void { + // remove listeners set up by this class + this.stream.removeListener('data', this.tryOnStreamData); + this.stream.removeListener('error', this.onStreamError); + this.stream.removeListener('end', this.onStreamEnd); + + // remove listeners set up by external consumers + for (const [name, listeners] of this._listeners.entries()) { + this._listeners.delete(name); + for (const [listener, disposable] of listeners) { + disposable.dispose(); + listeners.delete(listener); + } + } + } + + /** + * Pauses the stream. + */ + public pause(): void { + this.stream.pause(); + } + + /** + * Resumes the stream if it has been paused. + * @throws if the decoder stream has already ended. + */ + public resume(): void { + assert( + !this.ended, + 'Cannot resume the stream because it has already ended.', + ); + + this.stream.resume(); + } + + /** + * Destroys(disposes) the stream. + */ + public destroy(): void { + this.dispose(); + } + + /** + * Removes a priorly-registered event listener for a specified event. + * + * Note! + * - the callback function must be the same as the one that was used when + * registering the event listener as it is used as an identifier to + * remove the listener + * - this method is idempotent and results in no-op if the listener is + * not found, therefore passing incorrect `callback` function may + * result in silent unexpected behaviour + */ + public removeListener(event: string, callback: Function): void { + for (const [nameName, listeners] of this._listeners.entries()) { + if (nameName !== event) { + continue; + } + + for (const [listener, disposable] of listeners) { + if (listener !== callback) { + continue; + } + + disposable.dispose(); + listeners.delete(listener); + } + } + } + + /** + * This method is called when the input stream ends. + */ + protected onStreamEnd(): void { + if (this.ended) { + return; + } + + this.ended = true; + this._onEnd.fire(); + } + + /** + * This method is called when the input stream emits an error. + * We re-emit the error here by default, but subclasses can + * override this method to handle the error differently. + */ + protected onStreamError(error: Error): void { + this._onError.fire(error); + } + + /** + * Consume all messages from the stream, blocking until the stream finishes. + * @throws if the decoder stream has already ended. + */ + public async consumeAll(): Promise { + assert( + !this.ended, + 'Cannot consume all messages of the stream that has already ended.', + ); + + const messages = []; + + for await (const maybeMessage of this) { + if (maybeMessage === null) { + break; + } + + messages.push(maybeMessage); + } + + return messages; + } + + /** + * Async iterator interface for the decoder. + * @throws if the decoder stream has already ended. + */ + [Symbol.asyncIterator](): AsyncIterator { + assert( + !this.ended, + 'Cannot iterate on messages of the stream that has already ended.', + ); + + const asyncDecoder = this._register(new AsyncDecoder(this)); + + return asyncDecoder[Symbol.asyncIterator](); + } + + public override dispose(): void { + this.onStreamEnd(); + + this.stream.destroy(); + this.removeAllListeners(); + super.dispose(); + } +} diff --git a/src/vs/base/common/codecs/types/ICodec.d.ts b/src/vs/base/common/codecs/types/ICodec.d.ts new file mode 100644 index 00000000000..cd8abb3ff67 --- /dev/null +++ b/src/vs/base/common/codecs/types/ICodec.d.ts @@ -0,0 +1,22 @@ +import { ReadableStream } from '../../stream.js'; + +/** + * A codec is an object capable of encoding/decoding a stream of data transforming its messages. + * Useful for abstracting a data transfer or protocol logic on top of a stream of bytes. + * + * For instance, if protocol messages need to be trasferred over `TCP` connection, a codec that + * encodes the messages into a sequence of bytes before sending it to a network socket. Likewise, + * on the other end of the connection, the same codec can decode the sequence of bytes back into + * a sequence of the protocol messages. + */ +export interface ICodec { + /** + * Encode a stream of `K`s into a stream of `T`s. + */ + encode: (value: ReadableStream) => ReadableStream; + + /** + * Decode a stream of `T`s into a stream of `K`s. + */ + decode: (value: ReadableStream) => ReadableStream; +} diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index ab4c9f92e06..594fcf63545 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { assert } from './assert.js'; + export function clamp(value: number, min: number, max: number): number { return Math.min(Math.max(value, min), max); } @@ -96,3 +98,63 @@ export function isPointWithinTriangle( return u >= 0 && v >= 0 && u + v < 1; } + +/** + * Function to get a (pseudo)random integer from a provided `max`...[`min`] range. + * Both `min` and `max` values are inclusive. The `min` value is optional and defaults + * to `0` if not explicitely specified. + * + * @throws in the next cases: + * - if provided `min` or `max` is not a number + * - if provided `min` or `max` is not finite + * - if provided `min` is larger than `max` value + * + * ## Examples + * + * Specifying a `max` value only uses `0` as the `min` value by default: + * + * ```typescript + * // get a random integer between 0 and 10 + * const randomInt = randomInt(10); + * + * assert( + * randomInt >= 0, + * 'Should be greater than or equal to 0.', + * ); + * + * assert( + * randomInt <= 10, + * 'Should be less than or equal to 10.', + * ); + * ``` + * * Specifying both `max` and `min` values: + * + * ```typescript + * // get a random integer between 5 and 8 + * const randomInt = randomInt(8, 5); + * + * assert( + * randomInt >= 5, + * 'Should be greater than or equal to 5.', + * ); + * + * assert( + * randomInt <= 8, + * 'Should be less than or equal to 8.', + * ); + * ``` + */ +export const 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.'); + + assert(isFinite(max), '"max" param is not finite.'); + assert(isFinite(min), '"min" param is not finite.'); + + assert(max > min, `"max"(${max}) param should be greater than "min"(${min}).`); + + const delta = max - min; + const randomFloat = delta * Math.random(); + + return Math.round(min + randomFloat); +}; diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 7d990dfcab8..990c8f1c6bc 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -186,7 +186,7 @@ export interface ITransformer { error?: IErrorTransformer; } -export function newWriteableStream(reducer: IReducer, options?: WriteableStreamOptions): WriteableStream { +export function newWriteableStream(reducer: IReducer | null, options?: WriteableStreamOptions): WriteableStream { return new WriteableStreamImpl(reducer, options); } @@ -221,7 +221,13 @@ class WriteableStreamImpl implements WriteableStream { private readonly pendingWritePromises: Function[] = []; - constructor(private reducer: IReducer, private options?: WriteableStreamOptions) { } + /** + * @param reducer a function that reduces the buffered data into a single object; + * because some objects can be complex and non-reducible, we also + * allow passing the explicit `null` value to skip the reduce step + * @param options stream options + */ + constructor(private reducer: IReducer | null, private options?: WriteableStreamOptions) { } pause(): void { if (this.state.destroyed) { @@ -396,18 +402,30 @@ class WriteableStreamImpl implements WriteableStream { } private flowData(): void { - if (this.buffer.data.length > 0) { + // if buffer is empty, nothing to do + if (this.buffer.data.length === 0) { + return; + } + + // if buffer data can be reduced into a single object, + // emit the reduced data + if (typeof this.reducer === 'function') { const fullDataBuffer = this.reducer(this.buffer.data); this.emitData(fullDataBuffer); + } else { + // otherwise emit each buffered data instance individually + for (const data of this.buffer.data) { + this.emitData(data); + } + } - this.buffer.data.length = 0; + this.buffer.data.length = 0; - // When the buffer is empty, resolve all pending writers - const pendingWritePromises = [...this.pendingWritePromises]; - this.pendingWritePromises.length = 0; - pendingWritePromises.forEach(pendingWritePromise => pendingWritePromise()); - } + // when the buffer is empty, resolve all pending writers + const pendingWritePromises = [...this.pendingWritePromises]; + this.pendingWritePromises.length = 0; + pendingWritePromises.forEach(pendingWritePromise => pendingWritePromise()); } private flowErrors(): void { diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 6ad28eaece0..54edfafe71f 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { assert } from './assert.js'; + /** * @returns whether the provided parameter is a JavaScript String or not. */ @@ -93,15 +95,52 @@ export function assertType(condition: unknown, type?: string): asserts condition /** * Asserts that the argument passed in is neither undefined nor null. + * + * @see {@link assertDefined} for a similar utility that leverages TS assertion functions to narrow down the type of `arg` to be non-nullable. */ -export function assertIsDefined(arg: T | null | undefined): T { - if (isUndefinedOrNull(arg)) { - throw new Error('Assertion Failed: argument is undefined or null'); - } +export function assertIsDefined(arg: T | null | undefined): NonNullable { + assert( + arg !== null && arg !== undefined, + 'Argument is `undefined` or `null`.', + ); return arg; } +/** + * Asserts that a provided `value` is `defined` - not `null` or `undefined`, + * throwing an error with the provided error or error message, while also + * narrowing down the type of the `value` to be `NonNullable` using TS + * assertion functions. + * + * @throws if the provided `value` is `null` or `undefined`. + * + * ## Examples + * + * ```typescript + * // an assert with an error message + * assertDefined('some value', 'String constant is not defined o_O.'); + * + * // `throws!` the provided error + * assertDefined(null, new Error('Should throw this error.')); + * + * // narrows down the type of `someValue` to be non-nullable + * const someValue: string | undefined | null = blackbox(); + * assertDefined(someValue, 'Some value must be defined.'); + * console.log(someValue.length); // now type of `someValue` is `string` + * ``` + * + * @see {@link assertIsDefined} for a similar utility but without assertion. + * @see {@link https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions typescript-3-7.html#assertion-functions} + */ +export function assertDefined(value: T, error: string | NonNullable): asserts value is NonNullable { + if (value === null || value === undefined) { + const errorToThrow = typeof error === 'string' ? new Error(error) : error; + + throw errorToThrow; + } +} + /** * Asserts that each argument passed in is neither undefined nor null. */ diff --git a/src/vs/base/test/common/numbers.test.ts b/src/vs/base/test/common/numbers.test.ts index ac544e76dfb..7916bf6710d 100644 --- a/src/vs/base/test/common/numbers.test.ts +++ b/src/vs/base/test/common/numbers.test.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import { isPointWithinTriangle } from '../../common/numbers.js'; +import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; +import { isPointWithinTriangle, randomInt } from '../../common/numbers.js'; suite('isPointWithinTriangle', () => { ensureNoDisposablesAreLeakedInTestSuite(); @@ -25,3 +25,176 @@ suite('isPointWithinTriangle', () => { assert.ok(result); }); }); + +suite('randomInt', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + /** + * Test helper that allows to run a test on the `randomInt()` + * utility with specified `max` and `min` values. + */ + const testRandomIntUtil = (max: number, min: number | undefined, testName: string) => { + suite(testName, () => { + let i = 0; + while (++i < 5) { + test(`should generate random boolean attempt#${i}`, async () => { + let iterations = 100; + while (iterations-- > 0) { + const int = randomInt(max, min); + + assert( + int <= max, + `Expected ${int} to be less than or equal to ${max}.` + ); + assert( + int >= (min ?? 0), + `Expected ${int} to be greater than or equal to ${min ?? 0}.`, + ); + } + }); + } + + test(`should include min and max`, async () => { + let iterations = 100; + const results = []; + while (iterations-- > 0) { + results.push(randomInt(max, min)); + } + + assert( + results.includes(max), + `Expected ${results} to include ${max}.`, + ); + assert( + results.includes(min ?? 0), + `Expected ${results} to include ${min ?? 0}.`, + ); + }); + }); + }; + + suite('positive numbers', () => { + testRandomIntUtil(5, 2, 'max: 5, min: 2'); + testRandomIntUtil(5, 0, 'max: 5, min: 0'); + testRandomIntUtil(5, undefined, 'max: 5, min: undefined'); + testRandomIntUtil(1, 0, 'max: 0, min: 0'); + }); + + suite('negative numbers', () => { + testRandomIntUtil(-2, -5, 'max: -2, min: -5'); + testRandomIntUtil(0, -5, 'max: 0, min: -5'); + testRandomIntUtil(0, -1, 'max: 0, min: -1'); + }); + + suite('split numbers', () => { + testRandomIntUtil(3, -1, 'max: 3, min: -1'); + testRandomIntUtil(2, -2, 'max: 2, min: -2'); + testRandomIntUtil(1, -3, 'max: 2, min: -2'); + }); + + suite('errors', () => { + test('should throw if "min" is == "max" #1', () => { + assert.throws(() => { + randomInt(200, 200); + }, `"max"(200) param should be greater than "min"(200)."`); + }); + + test('should throw if "min" is == "max" #2', () => { + assert.throws(() => { + randomInt(2, 2); + }, `"max"(2) param should be greater than "min"(2)."`); + }); + + test('should throw if "min" is == "max" #3', () => { + assert.throws(() => { + randomInt(0); + }, `"max"(0) param should be greater than "min"(0)."`); + }); + + test('should throw if "min" is > "max" #1', () => { + assert.throws(() => { + randomInt(2, 3); + }, `"max"(2) param should be greater than "min"(3)."`); + }); + + test('should throw if "min" is > "max" #2', () => { + assert.throws(() => { + randomInt(999, 2000); + }, `"max"(999) param should be greater than "min"(2000)."`); + }); + + test('should throw if "min" is > "max" #3', () => { + assert.throws(() => { + randomInt(0, 1); + }, `"max"(0) param should be greater than "min"(1)."`); + }); + + test('should throw if "min" is > "max" #4', () => { + assert.throws(() => { + randomInt(-5, 2); + }, `"max"(-5) param should be greater than "min"(2)."`); + }); + + test('should throw if "min" is > "max" #5', () => { + assert.throws(() => { + randomInt(-5, 0); + }, `"max"(-5) param should be greater than "min"(0)."`); + }); + + test('should throw if "min" is > "max" #6', () => { + assert.throws(() => { + randomInt(-5); + }, `"max"(-5) param should be greater than "min"(0)."`); + }); + + test('should throw if "max" is `NaN`', () => { + assert.throws(() => { + randomInt(NaN); + }, `"max" param is not a number."`); + }); + + test('should throw if "min" is `NaN`', () => { + assert.throws(() => { + randomInt(5, NaN); + }, `"min" param is not a number."`); + }); + + suite('infinite arguments', () => { + test('should throw if "max" is infinite [Infinity]', () => { + assert.throws(() => { + randomInt(Infinity); + }, `"max" param is not finite."`); + }); + + test('should throw if "max" is infinite [-Infinity]', () => { + assert.throws(() => { + randomInt(-Infinity); + }, `"max" param is not finite."`); + }); + + test('should throw if "max" is infinite [+Infinity]', () => { + assert.throws(() => { + randomInt(+Infinity); + }, `"max" param is not finite."`); + }); + + test('should throw if "min" is infinite [Infinity]', () => { + assert.throws(() => { + randomInt(Infinity, Infinity); + }, `"max" param is not finite."`); + }); + + test('should throw if "min" is infinite [-Infinity]', () => { + assert.throws(() => { + randomInt(Infinity, -Infinity); + }, `"max" param is not finite."`); + }); + + test('should throw if "min" is infinite [+Infinity]', () => { + assert.throws(() => { + randomInt(Infinity, +Infinity); + }, `"max" param is not finite."`); + }); + }); + }); +}); diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index b25769c2415..83e81e93aae 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -5,10 +5,10 @@ import assert from 'assert'; import { timeout } from '../../common/async.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; import { bufferToReadable, VSBuffer } from '../../common/buffer.js'; import { CancellationTokenSource } from '../../common/cancellation.js'; import { consumeReadable, consumeStream, isReadable, isReadableBufferedStream, isReadableStream, listenStream, newWriteableStream, peekReadable, peekStream, prefixedReadable, prefixedStream, Readable, ReadableStream, toReadable, toStream, transform } from '../../common/stream.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; suite('Stream', () => { @@ -91,6 +91,104 @@ suite('Stream', () => { assert.strictEqual(chunks.length, 4); }); + test('stream with non-reducible messages', () => { + /** + * A complex object that cannot be reduced to a single object. + */ + class TestMessage { + constructor(public value: string) { } + } + + const stream = newWriteableStream(null); + + let error = false; + stream.on('error', e => { + error = true; + }); + + let end = false; + stream.on('end', () => { + end = true; + }); + + stream.write(new TestMessage('Hello')); + + const chunks: TestMessage[] = []; + stream.on('data', data => { + chunks.push(data); + }); + + assert( + chunks[0] instanceof TestMessage, + 'Message `0` must be an instance of `TestMessage`.', + ); + assert.strictEqual(chunks[0].value, 'Hello'); + + stream.write(new TestMessage('World')); + + assert( + chunks[1] instanceof TestMessage, + 'Message `1` must be an instance of `TestMessage`.', + ); + assert.strictEqual(chunks[1].value, 'World'); + + assert.strictEqual(error, false); + assert.strictEqual(end, false); + + stream.pause(); + stream.write(new TestMessage('1')); + stream.write(new TestMessage('2')); + stream.write(new TestMessage('3')); + + assert.strictEqual(chunks.length, 2); + + stream.resume(); + + assert.strictEqual(chunks.length, 5); + + assert( + chunks[2] instanceof TestMessage, + 'Message `2` must be an instance of `TestMessage`.', + ); + assert.strictEqual(chunks[2].value, '1'); + + assert( + chunks[3] instanceof TestMessage, + 'Message `3` must be an instance of `TestMessage`.', + ); + assert.strictEqual(chunks[3].value, '2'); + + assert( + chunks[4] instanceof TestMessage, + 'Message `4` must be an instance of `TestMessage`.', + ); + assert.strictEqual(chunks[4].value, '3'); + + stream.error(new Error()); + assert.strictEqual(error, true); + + error = false; + stream.error(new Error()); + assert.strictEqual(error, true); + + stream.end(new TestMessage('Final Bit')); + assert.strictEqual(chunks.length, 6); + + assert( + chunks[5] instanceof TestMessage, + 'Message `5` must be an instance of `TestMessage`.', + ); + assert.strictEqual(chunks[5].value, 'Final Bit'); + + + assert.strictEqual(end, true); + + stream.destroy(); + + stream.write(new TestMessage('Unexpected')); + assert.strictEqual(chunks.length, 6); + }); + test('WriteableStream - end with empty string works', async () => { const reducer = (strings: string[]) => strings.length > 0 ? strings.join() : 'error'; const stream = newWriteableStream(reducer); diff --git a/src/vs/base/test/common/types.test.ts b/src/vs/base/test/common/types.test.ts index 8f6eba0d0f3..da005539201 100644 --- a/src/vs/base/test/common/types.test.ts +++ b/src/vs/base/test/common/types.test.ts @@ -6,6 +6,7 @@ import assert from 'assert'; import * as types from '../../common/types.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; +import { assertDefined } from '../../common/types.js'; suite('Types', () => { @@ -174,6 +175,135 @@ suite('Types', () => { assert.strictEqual(res[2], 'Hello'); }); + suite('assertDefined', () => { + test('should not throw if `value` is defined (bool)', async () => { + assert.doesNotThrow(function () { + assertDefined(true, 'Oops something happened.'); + }); + }); + + test('should not throw if `value` is defined (number)', async () => { + assert.doesNotThrow(function () { + assertDefined(5, 'Oops something happened.'); + }); + }); + + test('should not throw if `value` is defined (zero)', async () => { + assert.doesNotThrow(function () { + assertDefined(0, 'Oops something happened.'); + }); + }); + + test('should not throw if `value` is defined (string)', async () => { + assert.doesNotThrow(function () { + assertDefined('some string', 'Oops something happened.'); + }); + }); + + test('should not throw if `value` is defined (empty string)', async () => { + assert.doesNotThrow(function () { + assertDefined('', 'Oops something happened.'); + }); + }); + + /** + * Note! API of `assert.throws()` is different in the browser + * and in Node.js, and it is not possible to use the same code + * here. Therefore we had to resort to the manual try/catch. + */ + const assertThrows = ( + testFunction: () => void, + errorMessage: string, + ) => { + let thrownError: Error | undefined; + + try { + testFunction(); + } catch (e) { + thrownError = e as Error; + } + + assertDefined(thrownError, 'Must throw an error.'); + assert( + thrownError instanceof Error, + 'Error must be an instance of `Error`.', + ); + + assert.strictEqual( + thrownError.message, + errorMessage, + 'Error must have correct message.', + ); + }; + + test('should throw if `value` is `null`', async () => { + const errorMessage = 'Uggh ohh!'; + assertThrows(() => { + assertDefined(null, errorMessage); + }, errorMessage); + }); + + test('should throw if `value` is `undefined`', async () => { + const errorMessage = 'Oh no!'; + assertThrows(() => { + assertDefined(undefined, new Error(errorMessage)); + }, errorMessage); + }); + + test('should throw assertion error by default', async () => { + const errorMessage = 'Uggh ohh!'; + let thrownError: Error | undefined; + try { + assertDefined(null, errorMessage); + } catch (e) { + thrownError = e as Error; + } + + assertDefined(thrownError, 'Must throw an error.'); + + assert( + thrownError instanceof Error, + 'Error must be an instance of `Error`.', + ); + + assert.strictEqual( + thrownError.message, + errorMessage, + 'Error must have correct message.', + ); + }); + + test('should throw provided error instance', async () => { + class TestError extends Error { + constructor(...args: ConstructorParameters) { + super(...args); + + this.name = 'TestError'; + } + } + + const errorMessage = 'Oops something hapenned.'; + const error = new TestError(errorMessage); + + let thrownError; + try { + assertDefined(null, error); + } catch (e) { + thrownError = e; + } + + assert( + thrownError instanceof TestError, + 'Error must be an instance of `TestError`.', + ); + assert.strictEqual( + thrownError.message, + errorMessage, + 'Error must have correct message.', + ); + }); + }); + test('validateConstraints', () => { types.validateConstraints([1, 'test', true], [Number, String, Boolean]); types.validateConstraints([1, 'test', true], ['number', 'string', 'boolean']); diff --git a/src/vs/editor/common/codecs/baseToken.ts b/src/vs/editor/common/codecs/baseToken.ts new file mode 100644 index 00000000000..9ebe3ad8abc --- /dev/null +++ b/src/vs/editor/common/codecs/baseToken.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRange, Range } from '../../../editor/common/core/range.js'; + +/** + * Base class for all tokens with a `range` that + * reflects token position in the original data. + */ +export abstract class BaseToken { + constructor( + private _range: Range, + ) { } + + public get range(): Range { + return this._range; + } + + /** + * Check if this token has the same range as another one. + */ + public sameRange(other: Range): boolean { + return this.range.equalsRange(other); + } + + /** + * Returns a string representation of the token. + */ + public abstract toString(): string; + + /** + * Check if this token is equal to another one. + */ + public equals(other: T): boolean { + if (!(other instanceof this.constructor)) { + return false; + } + + return this.sameRange(other.range); + } + + /** + * Change `range` of the token with provided range components. + */ + public withRange(components: Partial): this { + this._range = new Range( + components.startLineNumber ?? this.range.startLineNumber, + components.startColumn ?? this.range.startColumn, + components.endLineNumber ?? this.range.endLineNumber, + components.endColumn ?? this.range.endColumn, + ); + + return this; + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts b/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts new file mode 100644 index 00000000000..3bd72e5bd73 --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts @@ -0,0 +1,213 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Line } from './tokens/line.js'; +import { Range } from '../../core/range.js'; +import { NewLine } from './tokens/newLine.js'; +import { assert } from '../../../../base/common/assert.js'; +import { CarriageReturn } from './tokens/carriageReturn.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { assertDefined } from '../../../../base/common/types.js'; +import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; + +/** + * Tokens produced by the `LinesDecoder`. + */ +export type TLineToken = Line | CarriageReturn | NewLine; + +/** + * The `decoder` part of the `LinesCodec` and is able to transform + * data from a binary stream into a stream of text lines(`Line`). + */ +export class LinesDecoder extends BaseDecoder { + /** + * Buffered received data yet to be processed. + */ + private buffer: VSBuffer = VSBuffer.alloc(0); + + /** + * The last emitted `Line` token, if any. The value is used + * to correctly emit remaining line range in the `onStreamEnd` + * method when underlying input stream ends and `buffer` still + * contains some data that must be emitted as the last line. + */ + private lastEmittedLine?: Line; + + /** + * Process data received from the input stream. + */ + protected override onStreamData(chunk: VSBuffer): void { + this.buffer = VSBuffer.concat([this.buffer, chunk]); + + this.processData(false); + } + + /** + * Process buffered data. + * + * @param streamEnded Flag that indicates if the input stream has ended, + * which means that is the last call of this method. + * @throws If internal logic implementation error is detected. + */ + private processData( + streamEnded: boolean, + ) { + // iterate over each line of the data buffer, emitting each line + // as a `Line` token followed by a `NewLine` token, if applies + while (this.buffer.byteLength > 0) { + // get line number based on a previously emitted line, if any + const lineNumber = this.lastEmittedLine + ? this.lastEmittedLine.range.startLineNumber + 1 + : 1; + + // find the `\r`, `\n`, or `\r\n` tokens in the data + const endOfLineTokens = this.findEndOfLineTokens(lineNumber); + const firstToken = endOfLineTokens[0]; + + // if no end-of-the-line tokens found, stop processing because we + // either (1)need more data to arraive or (2)the stream has ended + // in the case (2) remaining data must be emitted as the last line + if (!firstToken) { + // (2) if `streamEnded`, we need to emit the whole remaining + // data as the last line immediately + if (streamEnded) { + this.emitLine(lineNumber, this.buffer.slice(0)); + } + + break; + } + + // emit the line found in the data as the `Line` token + this.emitLine(lineNumber, this.buffer.slice(0, firstToken.range.startColumn - 1)); + + // must always hold true as the `emitLine` above sets this + assertDefined( + this.lastEmittedLine, + 'No last emitted line found.', + ); + + // emit the end-of-the-line tokens + let startColumn = this.lastEmittedLine.range.endColumn; + for (const token of endOfLineTokens) { + const endColumn = startColumn + token.byte.byteLength; + // emit the token updating its column start/end numbers based on + // the emitted line text length and previous end-of-the-line token + this._onData.fire(token.withRange({ startColumn, endColumn })); + // shorten the data buffer by the length of the token + this.buffer = this.buffer.slice(token.byte.byteLength); + // update the start column for the next token + startColumn = endColumn; + } + } + + // if the stream has ended, assert that the input data buffer is now empty + // otherwise we have a logic error and leaving some buffered data behind + if (streamEnded) { + assert( + this.buffer.byteLength === 0, + 'Expected the input data buffer to be empty when the stream ends.', + ); + } + } + + /** + * Find the end of line tokens in the data buffer. + * Can return: + * - [`\r`, `\n`] tokens if the sequence is found + * - [`\r`] token if only the carriage return is found + * - [`\n`] token if only the newline is found + * - an `empty array` if no end of line tokens found + */ + private findEndOfLineTokens( + lineNumber: number, + ): (CarriageReturn | NewLine)[] { + const result = []; + + // find the first occurrence of the carriage return and newline tokens + const carriageReturnIndex = this.buffer.indexOf(CarriageReturn.byte); + const newLineIndex = this.buffer.indexOf(NewLine.byte); + + // if the `\r` comes before the `\n`(if `\n` present at all) + if (carriageReturnIndex >= 0 && (carriageReturnIndex < newLineIndex || newLineIndex === -1)) { + // add the carriage return token first + result.push( + new CarriageReturn(new Range( + lineNumber, + (carriageReturnIndex + 1), + lineNumber, + (carriageReturnIndex + 1) + CarriageReturn.byte.byteLength, + )), + ); + + // if the `\r\n` sequence + if (newLineIndex === carriageReturnIndex + 1) { + // add the newline token to the result + result.push( + new NewLine(new Range( + lineNumber, + (newLineIndex + 1), + lineNumber, + (newLineIndex + 1) + NewLine.byte.byteLength, + )), + ); + } + + if (this.buffer.byteLength > carriageReturnIndex + 1) { + // either `\r` or `\r\n` cases found + return result; + } + + return []; + } + + // no `\r`, but there is `\n` + if (newLineIndex >= 0) { + result.push( + new NewLine(new Range( + lineNumber, + (newLineIndex + 1), + lineNumber, + (newLineIndex + 1) + NewLine.byte.byteLength, + )), + ); + } + + // neither `\r` nor `\n` found, no end of line found at all + return result; + } + + /** + * Emit a provided line as the `Line` token to the output stream. + */ + private emitLine( + lineNumber: number, // Note! 1-based indexing + lineBytes: VSBuffer, + ): void { + + const line = new Line(lineNumber, lineBytes.toString()); + this._onData.fire(line); + + // store the last emitted line so we can use it when we need + // to send the remaining line in the `onStreamEnd` method + this.lastEmittedLine = line; + + // shorten the data buffer by the length of the line emitted + this.buffer = this.buffer.slice(lineBytes.byteLength); + } + + /** + * Handle the end of the input stream - if the buffer still has some data, + * emit it as the last available line token before firing the `onEnd` event. + */ + protected override onStreamEnd(): void { + // if the input data buffer is not empty when the input stream ends, emit + // the remaining data as the last line before firing the `onEnd` event + if (this.buffer.byteLength > 0) { + this.processData(true); + } + + super.onStreamEnd(); + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts new file mode 100644 index 00000000000..5120f4ac322 --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { Line } from './line.js'; +import { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { VSBuffer } from '../../../../../base/common/buffer.js'; + +/** + * Token that represent a `carriage return` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class CarriageReturn extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '\r'; + + /** + * The byte representation of the {@link symbol}. + */ + public static readonly byte = VSBuffer.fromString(CarriageReturn.symbol); + + /** + * The byte representation of the token. + */ + public get byte() { + return CarriageReturn.byte; + } + + /** + * Create new `CarriageReturn` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): CarriageReturn { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new CarriageReturn(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `carriage-return${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/line.ts b/src/vs/editor/common/codecs/linesCodec/tokens/line.ts new file mode 100644 index 00000000000..6669169967f --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/tokens/line.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { assert } from '../../../../../base/common/assert.js'; +import { Range } from '../../../../../editor/common/core/range.js'; + +/** + * Token representing a line of text with a `range` which + * reflects the line's position in the original data. + */ +export class Line extends BaseToken { + constructor( + // the line index + // Note! 1-based indexing + lineNumber: number, + // the line contents + public readonly text: string, + ) { + assert( + !isNaN(lineNumber), + `The line number must not be a NaN.`, + ); + + assert( + lineNumber > 0, + `The line number must be >= 1, got "${lineNumber}".`, + ); + + super( + new Range( + lineNumber, + 1, + lineNumber, + text.length + 1, + ), + ); + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.equals(other)) { + return false; + } + + if (!(other instanceof Line)) { + return false; + } + + return this.text === other.text; + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `line("${this.text}")${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts new file mode 100644 index 00000000000..19b80dd88a3 --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Line } from './line.js'; +import { BaseToken } from '../../baseToken.js'; +import { VSBuffer } from '../../../../../base/common/buffer.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a `new line` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class NewLine extends BaseToken { + /** + * The underlying symbol of the `NewLine` token. + */ + public static readonly symbol: string = '\n'; + + /** + * The byte representation of the {@link symbol}. + */ + public static readonly byte = VSBuffer.fromString(NewLine.symbol); + + /** + * The byte representation of the token. + */ + public get byte() { + return NewLine.byte; + } + + /** + * Create new `NewLine` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): NewLine { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new NewLine( + Range.fromPositions(startPosition, endPosition), + ); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `newline${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts b/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts new file mode 100644 index 00000000000..64173eceabd --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { FormFeed } from './tokens/formFeed.js'; +import { Tab } from '../simpleCodec/tokens/tab.js'; +import { Word } from '../simpleCodec/tokens/word.js'; +import { VerticalTab } from './tokens/verticalTab.js'; +import { Space } from '../simpleCodec/tokens/space.js'; +import { NewLine } from '../linesCodec/tokens/newLine.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { ReadableStream } from '../../../../base/common/stream.js'; +import { CarriageReturn } from '../linesCodec/tokens/carriageReturn.js'; +import { LinesDecoder, TLineToken } from '../linesCodec/linesDecoder.js'; +import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; + +/** + * A token type that this decoder can handle. + */ +export type TSimpleToken = Word | Space | Tab | VerticalTab | NewLine | FormFeed | CarriageReturn; + +/** + * Characters that stop a "word" sequence. + * Note! the `\r` and `\n` are excluded from the list because this decoder based on `LinesDecoder` which + * already handles the `carriagereturn`/`newline` cases and emits lines that don't contain them. + */ +const STOP_CHARACTERS = [Space.symbol, Tab.symbol, VerticalTab.symbol, FormFeed.symbol]; + +/** + * A decoder that can decode a stream of `Line`s into a stream + * of simple token, - `Word`, `Space`, `Tab`, `NewLine`, etc. + */ +export class SimpleDecoder extends BaseDecoder { + constructor( + stream: ReadableStream, + ) { + super(new LinesDecoder(stream)); + } + + protected override onStreamData(token: TLineToken): void { + // re-emit new line tokens + if (token instanceof CarriageReturn || token instanceof NewLine) { + this._onData.fire(token); + + return; + } + + // loop through the text separating it into `Word` and `Space` tokens + let i = 0; + while (i < token.text.length) { + // index is 0-based, but column numbers are 1-based + const columnNumber = i + 1; + + // if a space character, emit a `Space` token and continue + if (token.text[i] === Space.symbol) { + this._onData.fire(Space.newOnLine(token, columnNumber)); + + i++; + continue; + } + + // if a tab character, emit a `Tab` token and continue + if (token.text[i] === Tab.symbol) { + this._onData.fire(Tab.newOnLine(token, columnNumber)); + + i++; + continue; + } + + // if a vertical tab character, emit a `VerticalTab` token and continue + if (token.text[i] === VerticalTab.symbol) { + this._onData.fire(VerticalTab.newOnLine(token, columnNumber)); + + i++; + continue; + } + + // if a form feed character, emit a `FormFeed` token and continue + if (token.text[i] === FormFeed.symbol) { + this._onData.fire(FormFeed.newOnLine(token, columnNumber)); + + i++; + continue; + } + + // if a non-space character, parse out the whole word and + // emit it, then continue from the last word character position + let word = ''; + while (i < token.text.length && !(STOP_CHARACTERS.includes(token.text[i]))) { + word += token.text[i]; + i++; + } + + this._onData.fire( + Word.newOnLine(word, token, columnNumber), + ); + } + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts new file mode 100644 index 00000000000..ab40192f459 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * Token that represent a `form feed` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class FormFeed extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '\f'; + + /** + * Create new `FormFeed` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): FormFeed { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new FormFeed(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `formfeed${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts new file mode 100644 index 00000000000..9961c38ece9 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a `space` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class Space extends BaseToken { + /** + * The underlying symbol of the `Space` token. + */ + public static readonly symbol: string = ' '; + + /** + * Create new `Space` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): Space { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new Space(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `space${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts new file mode 100644 index 00000000000..aab11327bc1 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a `tab` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class Tab extends BaseToken { + /** + * The underlying symbol of the `Tab` token. + */ + public static readonly symbol: string = '\t'; + + /** + * Create new `Tab` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): Tab { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new Tab(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `tab${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts new file mode 100644 index 00000000000..11e5ca6efab --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * Token that represent a `vertical tab` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class VerticalTab extends BaseToken { + /** + * The underlying symbol of the `VerticalTab` token. + */ + public static readonly symbol: string = '\v'; + + /** + * Create new `VerticalTab` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): VerticalTab { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new VerticalTab(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `vtab${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts new file mode 100644 index 00000000000..fc3cefa79be --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a word - a set of continuous + * characters without stop characters, like a `space`, + * a `tab`, or a `new line`. + */ +export class Word extends BaseToken { + constructor( + /** + * The word range. + */ + range: Range, + + /** + * The string value of the word. + */ + public readonly text: string, + ) { + super(range); + } + + /** + * Create new `Word` token with the given `text` and the range + * inside the given `Line` at the specified `column number`. + */ + public static newOnLine( + text: string, + line: Line, + atColumnNumber: number, + ): Word { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + text.length); + + return new Word( + Range.fromPositions(startPosition, endPosition), + text, + ); + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.equals(other)) { + return false; + } + + if (!(other instanceof Word)) { + return false; + } + + return this.text === other.text; + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `word("${this.text.slice(0, 8)}")${this.range}`; + } +} diff --git a/src/vs/editor/test/common/codecs/linesDecoder.test.ts b/src/vs/editor/test/common/codecs/linesDecoder.test.ts new file mode 100644 index 00000000000..b3e6c91be13 --- /dev/null +++ b/src/vs/editor/test/common/codecs/linesDecoder.test.ts @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TestDecoder } from '../utils/testDecoder.js'; +import { Range } from '../../../common/core/range.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { newWriteableStream } from '../../../../base/common/stream.js'; +import { Line } from '../../../common/codecs/linesCodec/tokens/line.js'; +import { NewLine } from '../../../common/codecs/linesCodec/tokens/newLine.js'; +import { CarriageReturn } from '../../../common/codecs/linesCodec/tokens/carriageReturn.js'; +import { LinesDecoder, TLineToken } from '../../../common/codecs/linesCodec/linesDecoder.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; + +/** + * A reusable test utility that asserts that a `LinesDecoder` instance + * correctly decodes `inputData` into a stream of `TLineToken` tokens. + * + * ## Examples + * + * ```typescript + * // create a new test utility instance + * const test = testDisposables.add(new TestLinesDecoder()); + * + * // run the test + * await test.run( + * ' hello world\n', + * [ + * new Line(1, ' hello world'), + * new NewLine(new Range(1, 13, 1, 14)), + * ], + * ); + */ +export class TestLinesDecoder extends TestDecoder { + constructor() { + const stream = newWriteableStream(null); + const decoder = new LinesDecoder(stream); + + super(stream, decoder); + } +} + +suite('LinesDecoder', () => { + suite('produces expected tokens', () => { + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); + + test('input starts with line data', async () => { + const test = testDisposables.add(new TestLinesDecoder()); + + await test.run( + ' hello world\nhow are you doing?\n\n 😊 \r ', + [ + new Line(1, ' hello world'), + new NewLine(new Range(1, 13, 1, 14)), + new Line(2, 'how are you doing?'), + new NewLine(new Range(2, 19, 2, 20)), + new Line(3, ''), + new NewLine(new Range(3, 1, 3, 2)), + new Line(4, ' 😊 '), + new CarriageReturn(new Range(4, 5, 4, 6)), + new Line(5, ' '), + ], + ); + }); + + test('input starts with a new line', async () => { + const test = testDisposables.add(new TestLinesDecoder()); + + await test.run( + '\nsome text on this line\n\n\nanother 💬 on this line\r\n🤫\n', + [ + new Line(1, ''), + new NewLine(new Range(1, 1, 1, 2)), + new Line(2, 'some text on this line'), + new NewLine(new Range(2, 23, 2, 24)), + new Line(3, ''), + new NewLine(new Range(3, 1, 3, 2)), + new Line(4, ''), + new NewLine(new Range(4, 1, 4, 2)), + new Line(5, 'another 💬 on this line'), + new CarriageReturn(new Range(5, 24, 5, 25)), + new NewLine(new Range(5, 25, 5, 26)), + new Line(6, '🤫'), + new NewLine(new Range(6, 3, 6, 4)), + ], + ); + }); + + test('input starts and ends with multiple new lines', async () => { + const test = testDisposables.add(new TestLinesDecoder()); + + await test.run( + '\n\n\r\nciao! 🗯️\t💭 💥 come\tva?\n\n\n\n\n', + [ + new Line(1, ''), + new NewLine(new Range(1, 1, 1, 2)), + new Line(2, ''), + new NewLine(new Range(2, 1, 2, 2)), + new Line(3, ''), + new CarriageReturn(new Range(3, 1, 3, 2)), + new NewLine(new Range(3, 2, 3, 3)), + new Line(4, 'ciao! 🗯️\t💭 💥 come\tva?'), + new NewLine(new Range(4, 25, 4, 26)), + new Line(5, ''), + new NewLine(new Range(5, 1, 5, 2)), + new Line(6, ''), + new NewLine(new Range(6, 1, 6, 2)), + new Line(7, ''), + new NewLine(new Range(7, 1, 7, 2)), + new Line(8, ''), + new NewLine(new Range(8, 1, 8, 2)), + ], + ); + }); + + test('single carriage return is treated as new line', async () => { + const test = testDisposables.add(new TestLinesDecoder()); + + await test.run( + '\r\rhaalo! 💥💥 how\'re you?\r ?!\r\n\r\n ', + [ + new Line(1, ''), + new CarriageReturn(new Range(1, 1, 1, 2)), + new Line(2, ''), + new CarriageReturn(new Range(2, 1, 2, 2)), + new Line(3, 'haalo! 💥💥 how\'re you?'), + new CarriageReturn(new Range(3, 24, 3, 25)), + new Line(4, ' ?!'), + new CarriageReturn(new Range(4, 4, 4, 5)), + new NewLine(new Range(4, 5, 4, 6)), + new Line(5, ''), + new CarriageReturn(new Range(5, 1, 5, 2)), + new NewLine(new Range(5, 2, 5, 3)), + new Line(6, ' '), + ], + ); + }); + }); +}); diff --git a/src/vs/editor/test/common/codecs/simpleDecoder.test.ts b/src/vs/editor/test/common/codecs/simpleDecoder.test.ts new file mode 100644 index 00000000000..2e57a1c8219 --- /dev/null +++ b/src/vs/editor/test/common/codecs/simpleDecoder.test.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TestDecoder } from '../utils/testDecoder.js'; +import { Range } from '../../../common/core/range.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { newWriteableStream } from '../../../../base/common/stream.js'; +import { Tab } from '../../../common/codecs/simpleCodec/tokens/tab.js'; +import { Word } from '../../../common/codecs/simpleCodec/tokens/word.js'; +import { Space } from '../../../common/codecs/simpleCodec/tokens/space.js'; +import { NewLine } from '../../../common/codecs/linesCodec/tokens/newLine.js'; +import { FormFeed } from '../../../common/codecs/simpleCodec/tokens/formFeed.js'; +import { VerticalTab } from '../../../common/codecs/simpleCodec/tokens/verticalTab.js'; +import { CarriageReturn } from '../../../common/codecs/linesCodec/tokens/carriageReturn.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { SimpleDecoder, TSimpleToken } from '../../../common/codecs/simpleCodec/simpleDecoder.js'; + +/** + * A reusable test utility that asserts that a `SimpleDecoder` instance + * correctly decodes `inputData` into a stream of `TSimpleToken` tokens. + * + * ## Examples + * + * ```typescript + * // create a new test utility instance + * const test = testDisposables.add(new TestSimpleDecoder()); + * + * // run the test + * await test.run( + * ' hello world\n', + * [ + * new Space(new Range(1, 1, 1, 2)), + * new Word(new Range(1, 2, 1, 7), 'hello'), + * new Space(new Range(1, 7, 1, 8)), + * new Word(new Range(1, 8, 1, 13), 'world'), + * new NewLine(new Range(1, 13, 1, 14)), + * ], + * ); + */ +export class TestSimpleDecoder extends TestDecoder { + constructor() { + const stream = newWriteableStream(null); + const decoder = new SimpleDecoder(stream); + + super(stream, decoder); + } +} + +suite('SimpleDecoder', () => { + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); + + test('produces expected tokens', async () => { + const test = testDisposables.add( + new TestSimpleDecoder(), + ); + + await test.run( + ' hello world\nhow are\t you?\v\n\n (test) [!@#$%^&*_+=]\f \n\t\t🤗❤ \t\n hey\vthere\r\n\r\n', + [ + // first line + new Space(new Range(1, 1, 1, 2)), + new Word(new Range(1, 2, 1, 7), 'hello'), + new Space(new Range(1, 7, 1, 8)), + new Word(new Range(1, 8, 1, 13), 'world'), + new NewLine(new Range(1, 13, 1, 14)), + // second line + new Word(new Range(2, 1, 2, 4), 'how'), + new Space(new Range(2, 4, 2, 5)), + new Word(new Range(2, 5, 2, 8), 'are'), + new Tab(new Range(2, 8, 2, 9)), + new Space(new Range(2, 9, 2, 10)), + new Word(new Range(2, 10, 2, 14), 'you?'), + new VerticalTab(new Range(2, 14, 2, 15)), + new NewLine(new Range(2, 15, 2, 16)), + // third line + new NewLine(new Range(3, 1, 3, 2)), + // fourth line + new Space(new Range(4, 1, 4, 2)), + new Space(new Range(4, 2, 4, 3)), + new Space(new Range(4, 3, 4, 4)), + new Word(new Range(4, 4, 4, 10), '(test)'), + new Space(new Range(4, 10, 4, 11)), + new Space(new Range(4, 11, 4, 12)), + new Word(new Range(4, 12, 4, 25), '[!@#$%^&*_+=]'), + new FormFeed(new Range(4, 25, 4, 26)), + new Space(new Range(4, 26, 4, 27)), + new Space(new Range(4, 27, 4, 28)), + new NewLine(new Range(4, 28, 4, 29)), + // fifth line + new Tab(new Range(5, 1, 5, 2)), + new Tab(new Range(5, 2, 5, 3)), + new Word(new Range(5, 3, 5, 6), '🤗❤'), + new Space(new Range(5, 6, 5, 7)), + new Tab(new Range(5, 7, 5, 8)), + new NewLine(new Range(5, 8, 5, 9)), + // sixth line + new Space(new Range(6, 1, 6, 2)), + new Word(new Range(6, 2, 6, 5), 'hey'), + new VerticalTab(new Range(6, 5, 6, 6)), + new Word(new Range(6, 6, 6, 11), 'there'), + new CarriageReturn(new Range(6, 11, 6, 12)), + new NewLine(new Range(6, 12, 6, 13)), + // seventh line + new CarriageReturn(new Range(7, 1, 7, 2)), + new NewLine(new Range(7, 2, 7, 3)), + ], + ); + }); +}); diff --git a/src/vs/editor/test/common/utils/testDecoder.ts b/src/vs/editor/test/common/utils/testDecoder.ts new file mode 100644 index 00000000000..e9ee9ce1067 --- /dev/null +++ b/src/vs/editor/test/common/utils/testDecoder.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { VSBuffer } from '../../../../base/common/buffer.js'; +import { randomInt } from '../../../../base/common/numbers.js'; +import { BaseToken } from '../../../common/codecs/baseToken.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { WriteableStream } from '../../../../base/common/stream.js'; +import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; + +/** + * (pseudo)Random boolean generator. + * + * ## Examples + * + * ```typsecript + * randomBoolean(); // generates either `true` or `false` + * ``` + */ +const randomBoolean = (): boolean => { + return Math.random() > 0.5; +}; + +/** + * A reusable test utility that asserts that the given decoder + * produces the expected `expectedTokens` sequence of tokens. + * + * ## Examples + * + * ```typescript + * const stream = newWriteableStream(null); + * const decoder = testDisposables.add(new LinesDecoder(stream)); + * + * // create a new test utility instance + * const test = testDisposables.add(new TestDecoder(stream, decoder)); + * + * // run the test + * await test.run( + * ' hello world\n', + * [ + * new Line(1, ' hello world'), + * new NewLine(new Range(1, 13, 1, 14)), + * ], + * ); + */ +export class TestDecoder> extends Disposable { + constructor( + private readonly stream: WriteableStream, + private readonly decoder: D, + ) { + super(); + + this._register(this.decoder); + } + + /** + * Run the test sending the `inputData` data to the stream and asserting + * that the decoder produces the `expectedTokens` sequence of tokens. + */ + public async run( + inputData: string, + expectedTokens: readonly T[], + ): Promise { + // write the data to the stream after a short delay to ensure + // that the the data is sent after the reading loop below + setTimeout(() => { + let inputDataBytes = VSBuffer.fromString(inputData); + + // write the input data to the stream in multiple random-length chunks + while (inputDataBytes.byteLength > 0) { + const dataToSend = inputDataBytes.slice(0, randomInt(inputDataBytes.byteLength)); + this.stream.write(dataToSend); + inputDataBytes = inputDataBytes.slice(dataToSend.byteLength); + } + + this.stream.end(); + }, 25); + + // randomly use either the `async iterator` or the `.consume()` + // variants of getting tokens, they both must yield equal results + const receivedTokens: T[] = []; + if (randomBoolean()) { + // test the `async iterator` code path + for await (const token of this.decoder) { + if (token === null) { + break; + } + + receivedTokens.push(token); + } + } else { + // test the `.consume()` code path + receivedTokens.push(...(await this.decoder.consumeAll())); + } + + for (let i = 0; i < expectedTokens.length; i++) { + const expectedToken = expectedTokens[i]; + const receivedtoken = receivedTokens[i]; + + assert( + receivedtoken.equals(expectedToken), + `Expected token '${i}' to be '${expectedToken}', got '${receivedtoken}'.`, + ); + } + + assert.strictEqual( + receivedTokens.length, + expectedTokens.length, + 'Must produce correct number of tokens.', + ); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 998fcc7afb3..39bb44149b6 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -84,6 +84,7 @@ import { ChatEditingSessionState, IChatEditingService, IChatEditingSession, Work import { IChatRequestVariableEntry, isPasteVariableEntry } from '../common/chatModel.js'; import { ChatRequestDynamicVariablePart } from '../common/chatParserTypes.js'; import { IChatFollowup } from '../common/chatService.js'; +import { IChatVariablesService } from '../common/chatVariables.js'; import { IChatResponseViewModel } from '../common/chatViewModel.js'; import { IChatHistoryEntry, IChatInputState, IChatWidgetHistoryService } from '../common/chatWidgetHistoryService.js'; import { ILanguageModelChatMetadata, ILanguageModelsService } from '../common/languageModels.js'; @@ -99,6 +100,7 @@ import { ChatEditingRemoveAllFilesAction, ChatEditingShowChangesAction } from '. import { ChatEditingSaveAllAction } from './chatEditorSaving.js'; import { ChatFollowups } from './chatFollowups.js'; import { IChatViewState } from './chatWidget.js'; +import { ChatFileReference } from './contrib/chatDynamicVariables/chatFileReference.js'; import { ChatImplicitContext } from './contrib/chatImplicitContext.js'; const $ = dom.$; @@ -150,12 +152,32 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge return this._attachmentModel; } - public getAttachedAndImplicitContext(): IChatRequestVariableEntry[] { + public getAttachedAndImplicitContext(sessionId: string): IChatRequestVariableEntry[] { const contextArr = [...this.attachmentModel.attachments]; if (this.implicitContext?.enabled && this.implicitContext.value) { contextArr.push(this.implicitContext.toBaseEntry()); } + // factor in nested file references into the implicit context + const variables = this.variableService.getDynamicVariables(sessionId); + for (const variable of variables) { + if (!(variable instanceof ChatFileReference)) { + continue; + } + + for (const childUri of variable.validFileReferenceUris) { + contextArr.push({ + id: variable.id, + name: basename(childUri.path), + value: childUri, + isSelection: false, + enabled: true, + isFile: true, + isDynamic: true, + }); + } + } + return contextArr; } @@ -287,6 +309,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge @ITextModelService private readonly textModelResolverService: ITextModelService, @IStorageService private readonly storageService: IStorageService, @ILabelService private readonly labelService: ILabelService, + @IChatVariablesService private readonly variableService: IChatVariablesService, ) { super(); diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 800e82d952e..3bb7d3b2285 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -1006,7 +1006,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } } - let attachedContext = this.inputPart.getAttachedAndImplicitContext(); + let attachedContext = this.inputPart.getAttachedAndImplicitContext(this.viewModel.sessionId); let workingSet: URI[] | undefined; if (this.location === ChatAgentLocation.EditingSession) { const currentEditingSession = this.chatEditingService.currentEditingSessionObs.get(); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts index 9d1a1e19450..f4a6d525ce8 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts @@ -15,7 +15,7 @@ import { ITextModelService } from '../../../../../editor/common/services/resolve import { localize } from '../../../../../nls.js'; import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; -import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { AnythingQuickAccessProviderRunOptions, IQuickAccessOptions } from '../../../../../platform/quickinput/common/quickAccess.js'; @@ -24,14 +24,23 @@ import { IChatWidget } from '../chat.js'; import { ChatWidget, IChatWidgetContrib } from '../chatWidget.js'; import { IChatRequestVariableValue, IChatVariablesService, IDynamicVariable } from '../../common/chatVariables.js'; import { ISymbolQuickPickItem } from '../../../search/browser/symbolsQuickAccess.js'; +import { ChatFileReference } from './chatDynamicVariables/chatFileReference.js'; +import { PromptFileReference } from '../../common/promptFileReference.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; export const dynamicVariableDecorationType = 'chat-dynamic-variable'; +/** + * Type of dynamic variables. Can be either a file reference or + * another dynamic variable (e.g., a `#sym`, `#kb`, etc.). + */ +type TDynamicVariable = IDynamicVariable | ChatFileReference; + export class ChatDynamicVariableModel extends Disposable implements IChatWidgetContrib { public static readonly ID = 'chatDynamicVariableModel'; - private _variables: IDynamicVariable[] = []; - get variables(): ReadonlyArray { + private _variables: TDynamicVariable[] = []; + get variables(): ReadonlyArray { return [...this._variables]; } @@ -42,8 +51,11 @@ export class ChatDynamicVariableModel extends Disposable implements IChatWidgetC constructor( private readonly widget: IChatWidget, @ILabelService private readonly labelService: ILabelService, + @IConfigurationService private readonly configService: IConfigurationService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); + this._register(widget.inputEditor.onDidChangeModelContent(e => { e.changes.forEach(c => { // Don't mutate entries in _variables, since they will be returned from the getter @@ -60,18 +72,23 @@ export class ChatDynamicVariableModel extends Disposable implements IChatWidgetC }]); this.widget.refreshParsedInput(); } + + // dispose the reference if possible before dropping it off + if ('dispose' in ref && typeof ref.dispose === 'function') { + ref.dispose(); + } + return null; } else if (Range.compareRangesUsingStarts(ref.range, c.range) > 0) { const delta = c.text.length - c.rangeLength; - return { - ...ref, - range: { - startLineNumber: ref.range.startLineNumber, - startColumn: ref.range.startColumn + delta, - endLineNumber: ref.range.endLineNumber, - endColumn: ref.range.endColumn + delta - } + ref.range = { + startLineNumber: ref.range.startLineNumber, + startColumn: ref.range.startColumn + delta, + endLineNumber: ref.range.endLineNumber, + endColumn: ref.range.endColumn + delta, }; + + return ref; } return ref; @@ -83,7 +100,15 @@ export class ChatDynamicVariableModel extends Disposable implements IChatWidgetC } getInputState(): any { - return this.variables; + return this.variables + .map((variable: TDynamicVariable) => { + // return underlying `IDynamicVariable` object for file references + if (variable instanceof ChatFileReference) { + return variable.reference; + } + + return variable; + }); } setInputState(s: any): void { @@ -91,14 +116,39 @@ export class ChatDynamicVariableModel extends Disposable implements IChatWidgetC s = []; } - this._variables = s.filter(isDynamicVariable); - this.updateDecorations(); + this.disposeVariables(); + this._variables = []; + + for (const variable of s) { + if (!isDynamicVariable(variable)) { + continue; + } + + this.addReference(variable); + } } addReference(ref: IDynamicVariable): void { - this._variables.push(ref); + // use `ChatFileReference` for file references and `IDynamicVariable` for other variables + const promptSnippetsEnabled = PromptFileReference.promptSnippetsEnabled(this.configService); + const variable = (ref.id === 'vscode.file' && promptSnippetsEnabled) + ? this.instantiationService.createInstance(ChatFileReference, ref) + : ref; + + this._variables.push(variable); this.updateDecorations(); this.widget.refreshParsedInput(); + + // if the `prompt snippets` feature is enabled, and file is a `prompt snippet`, + // start resolving nested file references immediatelly and subscribe to updates + if (variable instanceof ChatFileReference && variable.isPromptSnippetFile) { + // subscribe to variable changes + variable.onUpdate(() => { + this.updateDecorations(); + }); + // start resolving the file references + variable.resolve(); + } } private updateDecorations(): void { @@ -120,6 +170,22 @@ export class ChatDynamicVariableModel extends Disposable implements IChatWidgetC return undefined; } } + + /** + * Dispose all existing variables. + */ + private disposeVariables(): void { + for (const variable of this._variables) { + if ('dispose' in variable && typeof variable.dispose === 'function') { + variable.dispose(); + } + } + } + + public override dispose() { + this.disposeVariables(); + super.dispose(); + } } /** diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts new file mode 100644 index 00000000000..2a5ecdf5048 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from '../../../../../../base/common/uri.js'; +import { assert } from '../../../../../../base/common/assert.js'; +import { IDynamicVariable } from '../../../common/chatVariables.js'; +import { IRange } from '../../../../../../editor/common/core/range.js'; +import { PromptFileReference } from '../../../common/promptFileReference.js'; +import { IFileService } from '../../../../../../platform/files/common/files.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; + +/** + * A wrapper class for an `IDynamicVariable` object that that adds functionality + * to parse nested file references of this variable. + * See {@link PromptFileReference} for details. + */ +export class ChatFileReference extends PromptFileReference implements IDynamicVariable { + /** + * @throws if the `data` reference is no an instance of `URI`. + */ + constructor( + public readonly reference: IDynamicVariable, + @IFileService fileService: IFileService, + @IConfigurationService configService: IConfigurationService, + ) { + const { data } = reference; + + assert( + data instanceof URI, + `Variable data must be an URI, got '${data}'.`, + ); + + super(data, fileService, configService); + } + + /** + * Note! below are the getters that simply forward to the underlying `IDynamicVariable` object; + * while we could implement the logic generically using the `Proxy` class here, it's hard + * to make Typescript to recognize this generic implementation correctly + */ + + public get id() { + return this.reference.id; + } + + public get range() { + return this.reference.range; + } + + public set range(range: IRange) { + this.reference.range = range; + } + + public get data(): URI { + return this.uri; + } + + public get prefix() { + return this.reference.prefix; + } + + public get isFile() { + return this.reference.isFile; + } + + public get fullName() { + return this.reference.fullName; + } + + public get icon() { + return this.reference.icon; + } + + public get modelDescription() { + return this.reference.modelDescription; + } +} diff --git a/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptCodec.ts b/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptCodec.ts new file mode 100644 index 00000000000..7522f5acd0b --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptCodec.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 { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { ReadableStream } from '../../../../../../base/common/stream.js'; +import { ChatPromptDecoder, TChatPromptToken } from './chatPromptDecoder.js'; +import { ICodec } from '../../../../../../base/common/codecs/types/ICodec.js'; + +/** + * `ChatPromptCodec` type is a `ICodec` with specific types for + * stream messages and return types of the `encode`/`decode` functions. + * @see {@linkcode ICodec} + */ +interface IChatPromptCodec extends ICodec { + /** + * Decode a stream of `VSBuffer`s into a stream of `TChatPromptToken`s. + * + * @see {@linkcode TChatPromptToken} + * @see {@linkcode VSBuffer} + * @see {@linkcode ChatPromptDecoder} + */ + decode: (value: ReadableStream) => ChatPromptDecoder; +} + +/** + * Codec that is capable to encode and decode tokens of an AI chatbot prompt message. + */ +export const ChatPromptCodec: IChatPromptCodec = Object.freeze({ + /** + * Encode a stream of `TChatPromptToken`s into a stream of `VSBuffer`s. + * + * @see {@linkcode ReadableStream} + * @see {@linkcode VSBuffer} + */ + encode: (_stream: ReadableStream): ReadableStream => { + throw new Error('The `encode` method is not implemented.'); + }, + + /** + * Decode a of `VSBuffer`s into a readable of `TChatPromptToken`s. + * + * @see {@linkcode TChatPromptToken} + * @see {@linkcode VSBuffer} + * @see {@linkcode ChatPromptDecoder} + * @see {@linkcode ReadableStream} + */ + decode: (stream: ReadableStream): ChatPromptDecoder => { + return new ChatPromptDecoder(stream); + }, +}); diff --git a/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptDecoder.ts b/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptDecoder.ts new file mode 100644 index 00000000000..57b7f0955b8 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/chatPromptDecoder.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { FileReference } from './tokens/fileReference.js'; +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { ReadableStream } from '../../../../../../base/common/stream.js'; +import { BaseDecoder } from '../../../../../../base/common/codecs/baseDecoder.js'; +import { Word } from '../../../../../../editor/common/codecs/simpleCodec/tokens/word.js'; +import { SimpleDecoder, TSimpleToken } from '../../../../../../editor/common/codecs/simpleCodec/simpleDecoder.js'; + +/** + * Tokens handled by the `ChatPromptDecoder` decoder. + */ +export type TChatPromptToken = FileReference; + +/** + * Decoder for the common chatbot prompt message syntax. + * For instance, the file references `#file:./path/file.md` are handled by this decoder. + */ +export class ChatPromptDecoder extends BaseDecoder { + constructor( + stream: ReadableStream, + ) { + super(new SimpleDecoder(stream)); + } + + protected override onStreamData(simpleToken: TSimpleToken): void { + // handle the word tokens only + if (!(simpleToken instanceof Word)) { + return; + } + + // handle file references only for now + const { text } = simpleToken; + if (!text.startsWith(FileReference.TOKEN_START)) { + return; + } + + this._onData.fire( + FileReference.fromWord(simpleToken), + ); + } +} diff --git a/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/tokens/fileReference.ts b/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/tokens/fileReference.ts new file mode 100644 index 00000000000..5a68344a757 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/codecs/chatPromptCodec/tokens/fileReference.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * 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 '../../../../../../../base/common/assert.js'; +import { Range } from '../../../../../../../editor/common/core/range.js'; +import { BaseToken } from '../../../../../../../editor/common/codecs/baseToken.js'; +import { Word } from '../../../../../../../editor/common/codecs/simpleCodec/tokens/word.js'; + +// Start sequence for a file reference token in a prompt. +const TOKEN_START: string = '#file:'; + +/** + * Object represents a file reference token inside a chatbot prompt. + */ +export class FileReference extends BaseToken { + // Start sequence for a file reference token in a prompt. + public static readonly TOKEN_START = TOKEN_START; + + constructor( + range: Range, + public readonly path: string, + ) { + super(range); + } + + /** + * Get full text of the file reference token. + */ + public get text(): string { + return `${TOKEN_START}${this.path}`; + } + + /** + * Create a file reference token out of a generic `Word`. + * @throws if the word does not conform to the expected format or if + * the reference is an invalid `URI`. + */ + public static fromWord(word: Word): FileReference { + const { text } = word; + + assert( + text.startsWith(TOKEN_START), + `The reference must start with "${TOKEN_START}", got ${text}.`, + ); + + const maybeReference = text.split(TOKEN_START); + + assert( + maybeReference.length === 2, + `The expected reference format is "${TOKEN_START}:filesystem-path", got ${text}.`, + ); + + const [first, second] = maybeReference; + + assert( + first === '', + `The reference must start with "${TOKEN_START}", got ${first}.`, + ); + + assert( + // Note! this accounts for both cases when second is `undefined` or `empty` + // and we don't care about rest of the "falsy" cases here + !!second, + `The reference path must be defined, got ${second}.`, + ); + + const reference = new FileReference( + word.range, + second, + ); + + return reference; + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.sameRange(other.range)) { + return false; + } + + if (!(other instanceof FileReference)) { + return false; + } + + return this.text === other.text; + } + + /** + * Return a string representation of the token. + */ + public override toString(): string { + return `file-ref("${this.text}")${this.range}`; + } +} diff --git a/src/vs/workbench/contrib/chat/common/promptFileReference.ts b/src/vs/workbench/contrib/chat/common/promptFileReference.ts new file mode 100644 index 00000000000..878c800d785 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/promptFileReference.ts @@ -0,0 +1,414 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from '../../../../base/common/uri.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { extUri } from '../../../../base/common/resources.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { Location } from '../../../../editor/common/languages.js'; +import { ChatPromptCodec } from './codecs/chatPromptCodec/chatPromptCodec.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { FileOpenFailed, NonPromptSnippetFile, RecursiveReference } from './promptFileReferenceErrors.js'; +import { FileChangesEvent, FileChangeType, IFileService, IFileStreamContent } from '../../../../platform/files/common/files.js'; + +/** + * Error conditions that may happen during the file reference resolution. + */ +export type TErrorCondition = FileOpenFailed | RecursiveReference | NonPromptSnippetFile; + +/** + * File extension for the prompt snippet files. + */ +const PROMP_SNIPPET_FILE_EXTENSION: string = '.prompt.md'; + +/** + * Configuration key for the prompt snippets feature. + */ +const PROMPT_SNIPPETS_CONFIG_KEY: string = 'chat.experimental.prompt-snippets'; + +/** + * Represents a file reference in the chatbot prompt, e.g. `#file:./path/to/file.md`. + * Contains logic to resolve all nested file references in the target file and all + * referenced child files recursively, if any. + * + * ## Examples + * + * ```typescript + * const fileReference = new PromptFileReference( + * URI.file('/path/to/file.md'), + * fileService, + * ); + * + * // subscribe to updates to the file reference tree + * fileReference.onUpdate(() => { + * // .. do something with the file reference tree .. + * // e.g. get URIs of all resolved file references in the tree + * const resolved = fileReference + * // get all file references as a flat array + * .flatten() + * // remove self from the list if only child references are needed + * .slice(1) + * // filter out unresolved references + * .filter(reference => reference.resolveFailed === flase) + * // convert to URIs only + * .map(reference => reference.uri); + * + * console.log(resolved); + * }); + * + * // *optional* if need to re-resolve file references when target files change + * // note that this does not sets up filesystem listeners for nested file references + * fileReference.addFilesystemListeners(); + * + * // start resolving the file reference tree; this can also be `await`ed if needed + * // to wait for the resolution on the main file reference to complete (the nested + * // references can still be resolving in the background) + * fileReference.resolve(); + * + * // don't forget to dispose when no longer needed! + * fileReference.dispose(); + * ``` + */ +export class PromptFileReference extends Disposable { + /** + * Child references of the current one. + */ + protected readonly children: PromptFileReference[] = []; + + /** + * The event is fired when nested prompt snippet references are updated, if any. + */ + private readonly _onUpdate = this._register(new Emitter()); + + private _errorCondition?: TErrorCondition; + /** + * If file reference resolution fails, this attribute will be set + * to an error instance that describes the error condition. + */ + public get errorCondition(): TErrorCondition | undefined { + return this._errorCondition; + } + + /** + * Whether file reference resolution was attempted at least once. + */ + private _resolveAttempted: boolean = false; + /** + * Whether file references resolution failed. + * Set to `undefined` if the `resolve` method hasn't been ever called yet. + */ + public get resolveFailed(): boolean | undefined { + if (!this._resolveAttempted) { + return undefined; + } + + return !!this._errorCondition; + } + + constructor( + private readonly _uri: URI | Location, + @IFileService private readonly fileService: IFileService, + @IConfigurationService private readonly configService: IConfigurationService, + ) { + super(); + this.onFilesChanged = this.onFilesChanged.bind(this); + + // make sure the variable is updated on file changes + // but only for the prompt snippet files + if (this.isPromptSnippetFile) { + this.addFilesystemListeners(); + } + } + + /** + * Subscribe to the `onUpdate` event. + * @param callback + */ + public onUpdate(callback: () => unknown) { + this._register(this._onUpdate.event(callback)); + } + + /** + * Check if the prompt snippets feature is enabled. + * @see {@link PROMPT_SNIPPETS_CONFIG_KEY} + */ + public static promptSnippetsEnabled( + configService: IConfigurationService, + ): boolean { + const value = configService.getValue(PROMPT_SNIPPETS_CONFIG_KEY); + + if (!value) { + return false; + } + + if (typeof value === 'string') { + return value.trim().toLowerCase() === 'true'; + } + + return !!value; + } + + /** + * Check if the current reference points to a prompt snippet file. + */ + public get isPromptSnippetFile(): boolean { + return this.uri.path.endsWith(PROMP_SNIPPET_FILE_EXTENSION); + } + + /** + * Associated URI of the reference. + */ + public get uri(): URI { + return this._uri instanceof URI + ? this._uri + : this._uri.uri; + } + + /** + * Get the directory name of the file reference. + */ + public get dirname() { + return URI.joinPath(this.uri, '..'); + } + + /** + * Check if the current reference points to a given resource. + */ + public sameUri(other: URI | Location): boolean { + const otherUri = other instanceof URI ? other : other.uri; + + return this.uri.toString() === otherUri.toString(); + } + + /** + * Add file system event listeners for the current file reference. + */ + private addFilesystemListeners(): this { + this._register( + this.fileService.onDidFilesChange(this.onFilesChanged), + ); + + return this; + } + + /** + * Event handler for the `onDidFilesChange` event. + */ + private onFilesChanged(event: FileChangesEvent) { + const fileChanged = event.contains(this.uri, FileChangeType.UPDATED); + const fileDeleted = event.contains(this.uri, FileChangeType.DELETED); + if (!fileChanged && !fileDeleted) { + return; + } + + // if file is changed or deleted, re-resolve the file reference + // in the case when the file is deleted, this should result in + // failure to open the file, so the `errorCondition` field will + // be updated to an appropriate error instance and the `children` + // field will be cleared up + this.resolve(); + } + + /** + * Get file stream, if the file exsists. + */ + private async getFileStream(): Promise { + try { + // read the file first + const result = await this.fileService.readFileStream(this.uri); + + // if file exists but not a prompt snippet file, set appropriate error + // condition and return null so we don't resolve nested references in it + if (this.uri.path.endsWith(PROMP_SNIPPET_FILE_EXTENSION) === false) { + this._errorCondition = new NonPromptSnippetFile(this.uri); + + return null; + } + + return result; + } catch (error) { + this._errorCondition = new FileOpenFailed(this.uri, error); + + return null; + } + } + + /** + * Resolve the current file reference on the disk and + * all nested file references that may exist in the file. + * + * @param waitForChildren Whether need to block until all child references are resolved. + */ + public async resolve( + waitForChildren: boolean = false, + ): Promise { + return await this.resolveReference(waitForChildren); + } + + /** + * Private implementation of the {@link resolve} method, that allows + * to pass `seenReferences` list to the recursive calls to prevent + * infinite file reference recursion. + */ + private async resolveReference( + waitForChildren: boolean = false, + seenReferences: string[] = [], + ): Promise { + // remove current error condition from the previous resolve attempt, if any + delete this._errorCondition; + + // dispose current child references, if any exist from a previous resolve + this.disposeChildren(); + + // to prevent infinite file recursion, we keep track of all references in + // the current branch of the file reference tree and check if the current + // file reference has been already seen before + if (seenReferences.includes(this.uri.path)) { + seenReferences.push(this.uri.path); + + this._errorCondition = new RecursiveReference(this.uri, seenReferences); + this._resolveAttempted = true; + this._onUpdate.fire(); + + return this; + } + + // we don't care if reading the file fails below, hence can add the path + // of the current reference to the `seenReferences` set immediately, - + // even if the file doesn't exist, we would never end up in the recursion + seenReferences.push(this.uri.path); + + // try to get stream for the contents of the file, it may + // fail to multiple reasons, e.g. file doesn't exist, etc. + const fileStream = await this.getFileStream(); + this._resolveAttempted = true; + + // failed to open the file, nothing to resolve + if (fileStream === null) { + this._onUpdate.fire(); + + return this; + } + + // get all file references in the file contents + const references = await ChatPromptCodec.decode(fileStream.value).consumeAll(); + + // recursively resolve all references and add to the `children` array + // + // Note! we don't register the children references as disposables here, because we dispose them + // explicitly in the `dispose` override method of this class. This is done to prevent + // the disposables store to be littered with already-disposed child instances due to + // the fact that the `resolve` method can be called multiple times on target file changes + const childPromises = []; + for (const reference of references) { + const childUri = extUri.resolvePath(this.dirname, reference.path); + + const child = new PromptFileReference( + childUri, + this.fileService, + this.configService, + ); + + // subscribe to child updates + child.onUpdate( + this._onUpdate.fire.bind(this._onUpdate), + ); + this.children.push(child); + + // start resolving the child in the background, including its children + // Note! we have to clone the `seenReferences` list here to ensure that + // different tree branches don't interfere with each other as we + // care about the parent references when checking for recursion + childPromises.push( + child.resolveReference(waitForChildren, [...seenReferences]), + ); + } + + // if should wait for all children to resolve, block here + if (waitForChildren) { + await Promise.all(childPromises); + } + + this._onUpdate.fire(); + + return this; + } + + /** + * Dispose current child file references. + */ + private disposeChildren(): this { + for (const child of this.children) { + child.dispose(); + } + + this.children.length = 0; + this._onUpdate.fire(); + + return this; + } + + /** + * Flatten the current file reference tree into a single array. + */ + public flatten(): readonly PromptFileReference[] { + const result = []; + + // then add self to the result + result.push(this); + + // get flattened children references first + for (const child of this.children) { + result.push(...child.flatten()); + } + + return result; + } + + /** + * Get list of all valid child references. + */ + public get validChildReferences(): readonly PromptFileReference[] { + return this.flatten() + // skip the root reference itself (this variable) + .slice(1) + // filter out unresolved references + .filter((reference) => { + return (reference.resolveFailed === false) || + (reference.errorCondition instanceof NonPromptSnippetFile); + }); + } + + /** + * Get list of all valid child references as URIs. + */ + public get validFileReferenceUris(): readonly URI[] { + return this.validChildReferences + .map(child => child.uri); + } + + /** + * Check if the current reference is equal to a given one. + */ + public equals(other: PromptFileReference): boolean { + if (!this.sameUri(other.uri)) { + return false; + } + + return true; + } + + /** + * Returns a string representation of this reference. + */ + public override toString() { + return `#file:${this.uri.path}`; + } + + public override dispose() { + this.disposeChildren(); + super.dispose(); + } +} diff --git a/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts b/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts new file mode 100644 index 00000000000..60da24e52a7 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from '../../../../base/common/uri.js'; + +/** + * Base resolve error class used when file reference resolution fails. + */ +abstract class ResolveError extends Error { + constructor( + public readonly uri: URI, + message?: string, + options?: ErrorOptions, + ) { + super(message, options); + } + + /** + * Check if provided object is of the same type as this error. + */ + public sameTypeAs(other: unknown): other is typeof this { + if (other === null || other === undefined) { + return false; + } + + return other instanceof this.constructor; + } + + /** + * Check if provided object is equal to this error. + */ + public equal(other: unknown): boolean { + return this.sameTypeAs(other); + } +} + +/** + * Error that reflects the case when attempt to open target file fails. + */ +export class FileOpenFailed extends ResolveError { + constructor( + uri: URI, + public readonly originalError: unknown, + ) { + super( + uri, + `Failed to open file '${uri.toString()}': ${originalError}.`, + ); + } +} + +/** + * Error that reflects the case when attempt resolve nested file + * references failes due to a recursive reference, e.g., + * + * ```markdown + * // a.md + * #file:b.md + * ``` + * + * ```markdown + * // b.md + * #file:a.md + * ``` + */ +export class RecursiveReference extends ResolveError { + constructor( + uri: URI, + public readonly recursivePath: string[], + ) { + const references = recursivePath.join(' -> '); + + super( + uri, + `Recursive references found: ${references}.`, + ); + } + + /** + * Returns a string representation of the recursive path. + */ + public get recursivePathString(): string { + return this.recursivePath.join(' -> '); + } + + /** + * Check if provided object is of the same type as this + * error, contains the same recursive path and URI. + */ + public override equal(other: unknown): other is this { + if (!this.sameTypeAs(other)) { + return false; + } + + if (this.uri.toString() !== other.uri.toString()) { + return false; + } + + return this.recursivePathString === other.recursivePathString; + } + + /** + * Returns a string representation of the error object. + */ + public override toString(): string { + return `"${this.message}"(${this.uri})`; + } +} + +/** + * Error that reflects the case when resource URI does not point to + * a prompt snippet file, hence was not attempted to be resolved. + */ +export class NonPromptSnippetFile extends ResolveError { + constructor( + uri: URI, + message: string = '', + ) { + + const suffix = message ? `: ${message}` : ''; + + super( + uri, + `Resource at ${uri.path} is not a prompt snippet file${suffix}`, + ); + } +} diff --git a/src/vs/workbench/contrib/chat/test/common/codecs/chatPromptCodec.test.ts b/src/vs/workbench/contrib/chat/test/common/codecs/chatPromptCodec.test.ts new file mode 100644 index 00000000000..01fd02cd1c0 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/codecs/chatPromptCodec.test.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { Range } from '../../../../../../editor/common/core/range.js'; +import { newWriteableStream } from '../../../../../../base/common/stream.js'; +import { TestDecoder } from '../../../../../../editor/test/common/utils/testDecoder.js'; +import { FileReference } from '../../../common/codecs/chatPromptCodec/tokens/fileReference.js'; +import { ChatPromptCodec } from '../../../common/codecs/chatPromptCodec/chatPromptCodec.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; +import { ChatPromptDecoder, TChatPromptToken } from '../../../common/codecs/chatPromptCodec/chatPromptDecoder.js'; + +/** + * A reusable test utility that asserts that a `ChatPromptDecoder` instance + * correctly decodes `inputData` into a stream of `TChatPromptToken` tokens. + * + * ## Examples + * + * ```typescript + * // create a new test utility instance + * const test = testDisposables.add(new TestChatPromptCodec()); + * + * // run the test + * await test.run( + * ' hello #file:./some-file.md world\n', + * [ + * new FileReference( + * new Range(1, 8, 1, 28), + * './some-file.md', + * ), + * ] + * ); + */ +export class TestChatPromptCodec extends TestDecoder { + constructor() { + const stream = newWriteableStream(null); + const decoder = ChatPromptCodec.decode(stream); + + super(stream, decoder); + } +} + +suite('ChatPromptCodec', () => { + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); + + test('produces expected tokens', async () => { + const test = testDisposables.add(new TestChatPromptCodec()); + + await test.run( + '#file:/etc/hosts some text\t\n for #file:./README.md\t testing\n ✔ purposes\n#file:LICENSE.md ✌ \t#file:.gitignore\n\n\n\t #file:/Users/legomushroom/repos/vscode ', + [ + new FileReference( + new Range(1, 1, 1, 1 + 16), + '/etc/hosts', + ), + new FileReference( + new Range(2, 7, 2, 7 + 17), + './README.md', + ), + new FileReference( + new Range(4, 1, 4, 1 + 16), + 'LICENSE.md', + ), + new FileReference( + new Range(4, 21, 4, 21 + 16), + '.gitignore', + ), + new FileReference( + new Range(7, 5, 7, 5 + 38), + '/Users/legomushroom/repos/vscode', + ), + ], + ); + }); +}); diff --git a/src/vs/workbench/contrib/chat/test/common/codecs/chatPromptDecoder.test.ts b/src/vs/workbench/contrib/chat/test/common/codecs/chatPromptDecoder.test.ts new file mode 100644 index 00000000000..2ebadb798f1 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/codecs/chatPromptDecoder.test.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { Range } from '../../../../../../editor/common/core/range.js'; +import { newWriteableStream } from '../../../../../../base/common/stream.js'; +import { TestDecoder } from '../../../../../../editor/test/common/utils/testDecoder.js'; +import { FileReference } from '../../../common/codecs/chatPromptCodec/tokens/fileReference.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; +import { ChatPromptDecoder, TChatPromptToken } from '../../../common/codecs/chatPromptCodec/chatPromptDecoder.js'; + +/** + * A reusable test utility that asserts that a `ChatPromptDecoder` instance + * correctly decodes `inputData` into a stream of `TChatPromptToken` tokens. + * + * ## Examples + * + * ```typescript + * // create a new test utility instance + * const test = testDisposables.add(new TestChatPromptDecoder()); + * + * // run the test + * await test.run( + * ' hello #file:./some-file.md world\n', + * [ + * new FileReference( + * new Range(1, 8, 1, 28), + * './some-file.md', + * ), + * ] + * ); + */ +export class TestChatPromptDecoder extends TestDecoder { + constructor( + ) { + const stream = newWriteableStream(null); + const decoder = new ChatPromptDecoder(stream); + + super(stream, decoder); + } +} + +suite('ChatPromptDecoder', () => { + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); + + test('produces expected tokens', async () => { + const test = testDisposables.add( + new TestChatPromptDecoder(), + ); + + await test.run( + '\nhaalo!\n message 👾 message #file:./path/to/file1.md \n\n \t#file:a/b/c/filename2.md\t🖖\t#file:other-file.md\nsome text #file:/some/file/with/absolute/path.md\t', + [ + new FileReference( + new Range(3, 21, 3, 21 + 24), + './path/to/file1.md', + ), + new FileReference( + new Range(5, 3, 5, 3 + 24), + 'a/b/c/filename2.md', + ), + new FileReference( + new Range(5, 31, 5, 31 + 19), + 'other-file.md', + ), + new FileReference( + new Range(6, 11, 6, 11 + 38), + '/some/file/with/absolute/path.md', + ), + ], + ); + }); +}); diff --git a/src/vs/workbench/contrib/chat/test/common/promptFileReference.test.ts b/src/vs/workbench/contrib/chat/test/common/promptFileReference.test.ts new file mode 100644 index 00000000000..e7256cf7099 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/promptFileReference.test.ts @@ -0,0 +1,474 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { URI } from '../../../../../base/common/uri.js'; +import { VSBuffer } from '../../../../../base/common/buffer.js'; +import { Schemas } from '../../../../../base/common/network.js'; +import { isWindows } from '../../../../../base/common/platform.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { FileService } from '../../../../../platform/files/common/fileService.js'; +import { NullPolicyService } from '../../../../../platform/policy/common/policy.js'; +import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js'; +import { PromptFileReference, TErrorCondition } from '../../common/promptFileReference.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { ConfigurationService } from '../../../../../platform/configuration/common/configurationService.js'; +import { InMemoryFileSystemProvider } from '../../../../../platform/files/common/inMemoryFilesystemProvider.js'; +import { FileOpenFailed, RecursiveReference, NonPromptSnippetFile } from '../../common/promptFileReferenceErrors.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; + +/** + * Represents a file system node. + */ +interface IFilesystemNode { + name: string; +} + +/** + * Represents a file node. + */ +interface IFile extends IFilesystemNode { + contents: string; +} + +/** + * Represents a folder node. + */ +interface IFolder extends IFilesystemNode { + children: (IFolder | IFile)[]; +} + +/** + * Represents a file reference with an expected + * error condition value for testing purposes. + */ +class ExpectedReference extends PromptFileReference { + constructor( + uri: URI, + public readonly error: TErrorCondition | undefined, + ) { + const nullLogService = new NullLogService(); + const nullPolicyService = new NullPolicyService(); + const nullFileService = new FileService(nullLogService); + const nullConfigService = new ConfigurationService( + URI.file('/config.json'), + nullFileService, + nullPolicyService, + nullLogService, + ); + super(uri, nullFileService, nullConfigService); + + this._register(nullFileService); + this._register(nullConfigService); + } + + /** + * Override the error condition getter to + * return the provided expected error value. + */ + public override get errorCondition() { + return this.error; + } +} + +/** + * A reusable test utility to test the `PromptFileReference` class. + */ +class TestPromptFileReference extends Disposable { + constructor( + private readonly fileStructure: IFolder, + private readonly rootFileUri: URI, + private readonly expectedReferences: ExpectedReference[], + @IFileService private readonly fileService: IFileService, + @IConfigurationService private readonly configService: IConfigurationService, + ) { + super(); + + // ensure all the expected references are disposed + for (const expectedReference of this.expectedReferences) { + this._register(expectedReference); + } + + // create in-memory file system + const fileSystemProvider = this._register(new InMemoryFileSystemProvider()); + this._register(this.fileService.registerProvider(Schemas.file, fileSystemProvider)); + } + + /** + * Run the test. + */ + public async run() { + // create the files structure on the disk + await this.createFolder( + this.fileService, + this.fileStructure, + ); + + // start resolving references for the specified root file + const rootReference = this._register(new PromptFileReference( + this.rootFileUri, + this.fileService, + this.configService, + )); + + // resolve the root file reference including all nested references + const resolvedReferences = (await rootReference.resolve(true)) + .flatten(); + + assert.strictEqual( + resolvedReferences.length, + this.expectedReferences.length, + [ + `\nExpected(${this.expectedReferences.length}): [\n ${this.expectedReferences.join('\n ')}\n]`, + `Received(${resolvedReferences.length}): [\n ${resolvedReferences.join('\n ')}\n]`, + ].join('\n') + ); + + for (let i = 0; i < this.expectedReferences.length; i++) { + const expectedReference = this.expectedReferences[i]; + const resolvedReference = resolvedReferences[i]; + + assert( + resolvedReference.equals(expectedReference), + [ + `Expected ${i}th resolved reference to be ${expectedReference}`, + `got ${resolvedReference}.`, + ].join(', '), + ); + + if (expectedReference.errorCondition === undefined) { + assert( + resolvedReference.errorCondition === undefined, + [ + `Expected ${i}th error condition to be 'undefined'`, + `got '${resolvedReference.errorCondition}'.`, + ].join(', '), + ); + continue; + } + + assert( + expectedReference.errorCondition.equal(resolvedReference.errorCondition), + [ + `Expected ${i}th error condition to be '${expectedReference.errorCondition}'`, + `got '${resolvedReference.errorCondition}'.`, + ].join(', '), + ); + } + } + + /** + * Create the provided filesystem folder structure. + */ + async createFolder( + fileService: IFileService, + folder: IFolder, + parentFolder?: URI, + ): Promise { + const folderUri = parentFolder + ? URI.joinPath(parentFolder, folder.name) + : URI.file(folder.name); + + if (await fileService.exists(folderUri)) { + await fileService.del(folderUri); + } + await fileService.createFolder(folderUri); + + for (const child of folder.children) { + const childUri = URI.joinPath(folderUri, child.name); + // create child file + if ('contents' in child) { + await fileService.writeFile(childUri, VSBuffer.fromString(child.contents)); + continue; + } + + // recursively create child filesystem structure + await this.createFolder(fileService, child, folderUri); + } + } +} + +suite('PromptFileReference (Unix)', function () { + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); + + let instantiationService: TestInstantiationService; + setup(async () => { + const nullPolicyService = new NullPolicyService(); + const nullLogService = testDisposables.add(new NullLogService()); + const nullFileService = testDisposables.add(new FileService(nullLogService)); + const nullConfigService = testDisposables.add(new ConfigurationService( + URI.file('/config.json'), + nullFileService, + nullPolicyService, + nullLogService, + )); + instantiationService = testDisposables.add(new TestInstantiationService()); + + instantiationService.stub(IFileService, nullFileService); + instantiationService.stub(ILogService, nullLogService); + instantiationService.stub(IConfigurationService, nullConfigService); + }); + + test('resolves nested file references', async function () { + if (isWindows) { + this.skip(); + } + + const rootFolderName = 'resolves-nested-file-references'; + const rootFolder = `/${rootFolderName}`; + const rootUri = URI.file(rootFolder); + + const test = testDisposables.add(instantiationService.createInstance(TestPromptFileReference, + /** + * The file structure to be created on the disk for the test. + */ + { + name: rootFolderName, + children: [ + { + name: 'file1.prompt.md', + contents: '## Some Header\nsome contents\n ', + }, + { + name: 'file2.prompt.md', + contents: '## Files\n\t- this file #file:folder1/file3.prompt.md \n\t- also this #file:./folder1/some-other-folder/file4.prompt.md please!\n ', + }, + { + name: 'folder1', + children: [ + { + name: 'file3.prompt.md', + contents: `\n\n\t- some seemingly random #file:${rootFolder}/folder1/some-other-folder/yetAnotherFolder🤭/another-file.prompt.md contents\n some more\t content`, + }, + { + name: 'some-other-folder', + children: [ + { + name: 'file4.prompt.md', + contents: 'this file has a non-existing #file:./some-non-existing/file.prompt.md\t\treference\n\nand some non-prompt #file:./some-non-prompt-file.js', + }, + { + name: 'file.txt', + contents: 'contents of a non-prompt-snippet file', + }, + { + name: 'yetAnotherFolder🤭', + children: [ + { + name: 'another-file.prompt.md', + contents: 'another-file.prompt.md contents\t #file:../file.txt', + }, + { + name: 'one_more_file_just_in_case.prompt.md', + contents: 'one_more_file_just_in_case.prompt.md contents', + }, + ], + }, + { + name: 'some-non-prompt-file.js', + contents: 'some-non-prompt-file.js contents', + }, + ], + }, + ], + }, + ], + }, + /** + * The root file path to start the resolve process from. + */ + URI.file(`/${rootFolderName}/file2.prompt.md`), + /** + * The expected references to be resolved. + */ + [ + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './file2.prompt.md'), + undefined, + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/file3.prompt.md'), + undefined, + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/yetAnotherFolder🤭/another-file.prompt.md'), + undefined, + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/file.txt'), + new NonPromptSnippetFile( + URI.joinPath(rootUri, './folder1/some-other-folder/file.txt'), + 'Ughh oh!', + ), + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/file4.prompt.md'), + undefined, + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/some-non-existing/file.prompt.md'), + new FileOpenFailed( + URI.joinPath(rootUri, './folder1/some-other-folder/some-non-existing/file.prompt.md'), + 'Some error message.', + ), + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/some-non-prompt-file.js'), + new NonPromptSnippetFile( + URI.joinPath(rootUri, './folder1/some-other-folder/some-non-prompt-file.js'), + 'Oh no!', + ), + )), + ] + )); + + await test.run(); + }); + + test('does not fall into infinite reference recursion', async function () { + if (isWindows) { + this.skip(); + } + + const rootFolderName = 'infinite-recursion'; + const rootFolder = `/${rootFolderName}`; + const rootUri = URI.file(rootFolder); + + const test = testDisposables.add(instantiationService.createInstance(TestPromptFileReference, + /** + * The file structure to be created on the disk for the test. + */ + { + name: rootFolderName, + children: [ + { + name: 'file1.md', + contents: '## Some Header\nsome contents\n ', + }, + { + name: 'file2.prompt.md', + contents: `## Files\n\t- this file #file:folder1/file3.prompt.md \n\t- also this #file:./folder1/some-other-folder/file4.prompt.md\n\n#file:${rootFolder}/folder1/some-other-folder/file5.prompt.md\t please!\n\t#file:./file1.md `, + }, + { + name: 'folder1', + children: [ + { + name: 'file3.prompt.md', + contents: `\n\n\t- some seemingly random #file:${rootFolder}/folder1/some-other-folder/yetAnotherFolder🤭/another-file.prompt.md contents\n some more\t content`, + }, + { + name: 'some-other-folder', + children: [ + { + name: 'file4.prompt.md', + contents: 'this file has a non-existing #file:../some-non-existing/file.prompt.md\t\treference', + }, + { + name: 'file5.prompt.md', + contents: 'this file has a relative recursive #file:../../file2.prompt.md\nreference\n ', + }, + { + name: 'yetAnotherFolder🤭', + children: [ + { + name: 'another-file.prompt.md', + // absolute path with recursion + contents: `some test goes\t\nhere #file:${rootFolder}/file2.prompt.md`, + }, + { + name: 'one_more_file_just_in_case.prompt.md', + contents: 'one_more_file_just_in_case.prompt.md contents', + }, + ], + }, + ], + }, + ], + }, + ], + }, + /** + * The root file path to start the resolve process from. + */ + URI.file(`/${rootFolderName}/file2.prompt.md`), + /** + * The expected references to be resolved. + */ + [ + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './file2.prompt.md'), + undefined, + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/file3.prompt.md'), + undefined, + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/yetAnotherFolder🤭/another-file.prompt.md'), + undefined, + )), + /** + * This reference should be resolved as + * a recursive reference error condition. + * (the absolute reference case) + */ + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './file2.prompt.md'), + new RecursiveReference( + URI.joinPath(rootUri, './file2.prompt.md'), + [ + '/infinite-recursion/file2.prompt.md', + '/infinite-recursion/folder1/file3.prompt.md', + '/infinite-recursion/folder1/some-other-folder/yetAnotherFolder🤭/another-file.prompt.md', + '/infinite-recursion/file2.prompt.md', + ], + ), + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/file4.prompt.md'), + undefined, + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-non-existing/file.prompt.md'), + new FileOpenFailed( + URI.joinPath(rootUri, './folder1/some-non-existing/file.prompt.md'), + 'Some error message.', + ), + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder/file5.prompt.md'), + undefined, + )), + /** + * This reference should be resolved as + * a recursive reference error condition. + * (the relative reference case) + */ + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './file2.prompt.md'), + new RecursiveReference( + URI.joinPath(rootUri, './file2.prompt.md'), + [ + '/infinite-recursion/file2.prompt.md', + '/infinite-recursion/folder1/some-other-folder/file5.prompt.md', + '/infinite-recursion/file2.prompt.md', + ], + ), + )), + testDisposables.add(new ExpectedReference( + URI.joinPath(rootUri, './file1.md'), + new NonPromptSnippetFile( + URI.joinPath(rootUri, './file1.md'), + 'Uggh oh!', + ), + )), + ] + )); + + await test.run(); + }); +}); From d6bf64517af4ce9d1eeab149269012926558142c Mon Sep 17 00:00:00 2001 From: DetachHead <57028336+DetachHead@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:42:08 +1000 Subject: [PATCH 284/479] remove `javascript.inlayHints.enumMemberValues.enabled` because javascript does not have enums --- extensions/typescript-language-features/package.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index c6363dfc48e..d93a488f08e 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -384,12 +384,6 @@ "markdownDescription": "%configuration.inlayHints.functionLikeReturnTypes.enabled%", "scope": "resource" }, - "javascript.inlayHints.enumMemberValues.enabled": { - "type": "boolean", - "default": false, - "markdownDescription": "%configuration.inlayHints.enumMemberValues.enabled%", - "scope": "resource" - }, "javascript.suggest.includeCompletionsForImportStatements": { "type": "boolean", "default": true, From 6be3f02482f73de545aea12e865e766a791eb4fd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 16 Dec 2024 15:50:05 -0800 Subject: [PATCH 285/479] Inline simple methods These don't add much --- .../src/typescriptServiceClient.ts | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 7e18022f36d..d9f47c1b85a 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -12,7 +12,7 @@ import { Schemes } from './configuration/schemes'; import { IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; import { DiagnosticKind, DiagnosticsManager } from './languageFeatures/diagnostics'; import { Logger } from './logging/logger'; -import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from './logging/telemetry'; +import { TelemetryReporter, VSCodeTelemetryReporter } from './logging/telemetry'; import Tracer from './logging/tracer'; import { ProjectType, inferredProjectCompilerOptions } from './tsconfig'; import { API } from './tsServer/api'; @@ -319,7 +319,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType public restartTsServer(fromUserAction = false): void { if (this.serverState.type === ServerState.Type.Running) { - this.info('Killing TS Server'); + this.logger.info('Killing TS Server'); this.isRestarting = true; this.serverState.server.kill(); } @@ -372,18 +372,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this._onReady!.promise.then(f); } - private info(message: string, ...data: any[]): void { - this.logger.info(message, ...data); - } - - private error(message: string, ...data: any[]): void { - this.logger.error(message, ...data); - } - - private logTelemetry(eventName: string, properties?: TelemetryProperties) { - this.telemetryReporter.logTelemetry(eventName, properties); - } - public ensureServiceStarted() { if (this.serverState.type !== ServerState.Type.Running) { this.startService(); @@ -392,15 +380,15 @@ export default class TypeScriptServiceClient extends Disposable implements IType private token: number = 0; private startService(resendModels: boolean = false): ServerState.State { - this.info(`Starting TS Server`); + this.logger.info(`Starting TS Server`); if (this.isDisposed) { - this.info(`Not starting server: disposed`); + this.logger.info(`Not starting server: disposed`); return ServerState.None; } if (this.hasServerFatallyCrashedTooManyTimes) { - this.info(`Not starting server: too many crashes`); + this.logger.info(`Not starting server: too many crashes`); return ServerState.None; } @@ -412,10 +400,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType version = this._versionManager.currentVersion; } - this.info(`Using tsserver from: ${version.path}`); + this.logger.info(`Using tsserver from: ${version.path}`); const nodePath = this._nodeVersionManager.currentVersion; if (nodePath) { - this.info(`Using Node installation from ${nodePath} to run TS Server`); + this.logger.info(`Using Node installation from ${nodePath} to run TS Server`); } this.resetWatchers(); @@ -451,7 +439,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType "typeScriptVersionSource": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this.logTelemetry('tsserver.spawned', { + this.telemetryReporter.logTelemetry('tsserver.spawned', { ...typeScriptServerEnvCommonProperties, localTypeScriptVersion: this.versionProvider.localVersion ? this.versionProvider.localVersion.displayName : '', typeScriptVersionSource: version.source, @@ -468,9 +456,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType } this.serverState = new ServerState.Errored(err, handle.tsServerLog); - this.error('TSServer errored with error.', err); + this.logger.error('TSServer errored with error.', err); if (handle.tsServerLog?.type === 'file') { - this.error(`TSServer log file: ${handle.tsServerLog.uri.fsPath}`); + this.logger.error(`TSServer log file: ${handle.tsServerLog.uri.fsPath}`); } /* __GDPR__ @@ -482,7 +470,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType ] } */ - this.logTelemetry('tsserver.error', { + this.telemetryReporter.logTelemetry('tsserver.error', { ...typeScriptServerEnvCommonProperties }); this.serviceExited(false, apiVersion); @@ -490,7 +478,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType handle.onExit((data: TypeScriptServerExitEvent) => { const { code, signal } = data; - this.error(`TSServer exited. Code: ${code}. Signal: ${signal}`); + this.logger.error(`TSServer exited. Code: ${code}. Signal: ${signal}`); // In practice, the exit code is an integer with no ties to any identity, // so it can be classified as SystemMetaData, rather than CallstackOrException. @@ -505,7 +493,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType "signal" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } } */ - this.logTelemetry('tsserver.exitWithCode', { + this.telemetryReporter.logTelemetry('tsserver.exitWithCode', { ...typeScriptServerEnvCommonProperties, code: code ?? undefined, signal: signal ?? undefined, @@ -517,7 +505,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } if (handle.tsServerLog?.type === 'file') { - this.info(`TSServer log file: ${handle.tsServerLog.uri.fsPath}`); + this.logger.info(`TSServer log file: ${handle.tsServerLog.uri.fsPath}`); } this.serviceExited(!this.isRestarting, apiVersion); this.isRestarting = false; @@ -678,7 +666,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType ] } */ - this.logTelemetry('serviceExited'); + this.telemetryReporter.logTelemetry('serviceExited'); } else if (diff < 60 * 1000 * 5 /* 5 Minutes */) { this.lastStart = Date.now(); if (!this._isPromptingAfterCrash) { @@ -956,14 +944,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this.logTelemetry('fatalError', { ...(error instanceof TypeScriptServerError ? error.telemetry : { command }) }); + this.telemetryReporter.logTelemetry('fatalError', { ...(error instanceof TypeScriptServerError ? error.telemetry : { command }) }); console.error(`A non-recoverable error occurred while executing tsserver command: ${command}`); if (error instanceof TypeScriptServerError && error.serverErrorText) { console.error(error.serverErrorText); } if (this.serverState.type === ServerState.Type.Running) { - this.info('Killing TS Server'); + this.logger.info('Killing TS Server'); const logfile = this.serverState.server.tsServerLog; this.serverState.server.kill(); if (error instanceof TypeScriptServerError) { @@ -1235,7 +1223,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } */ // __GDPR__COMMENT__: Other events are defined by TypeScript. - this.logTelemetry(telemetryData.telemetryEventName, properties); + this.telemetryReporter.logTelemetry(telemetryData.telemetryEventName, properties); } private configurePlugin(pluginName: string, configuration: {}): any { From 19c7c0190d58569f06e2d97f11573ea31360ba07 Mon Sep 17 00:00:00 2001 From: tcostew <143664112+tcostew@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:01:11 -0500 Subject: [PATCH 286/479] Allow Github Copilot chat to appear in QuickAccess (#210805) --- .../workbench/contrib/search/browser/anythingQuickAccess.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index 9d50072998d..c08b3518cf7 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -485,8 +485,9 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider Date: Mon, 16 Dec 2024 17:06:07 -0800 Subject: [PATCH 287/479] Use safer escaping for css url strings Fixes #236122 For css `\0000xx` escaping, if I'm understanding the spec correctly it turns out they eat the space character following them: https://www.w3.org/TR/CSS2/syndata.html#characters My previous fix didn't account for this. Instead I think a safer fix is to switch to use `Css.escape`. This often over-escapes the string by converting characters that don't need it but should be safe and it fixes the original issue --- src/vs/base/browser/cssValue.ts | 2 +- .../browser/services/decorationRenderOptions.test.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/cssValue.ts b/src/vs/base/browser/cssValue.ts index 6ee7fff792a..84cee8522ae 100644 --- a/src/vs/base/browser/cssValue.ts +++ b/src/vs/base/browser/cssValue.ts @@ -62,7 +62,7 @@ export function asCSSUrl(uri: URI | null | undefined): CssFragment { if (!uri) { return asFragment(`url('')`); } - return inline`url(${stringValue(FileAccess.uriToBrowserUri(uri).toString(true))})`; + return inline`url('${asFragment(CSS.escape(FileAccess.uriToBrowserUri(uri).toString(true)))}')`; } export function className(value: string, escapingExpected = false): CssFragment { diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index 74555b9a823..9608d1d779a 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -44,7 +44,7 @@ suite('Decoration Render Options', () => { const styleSheet = s.globalStyleSheet; store.add(s.registerDecorationType('test', 'example', options)); const sheet = readStyleSheet(styleSheet); - assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0); + assert(sheet.indexOf(`{background:url('${CSS.escape('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png')}') center center no-repeat;background-size:contain;}`) >= 0); assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0); }); @@ -111,7 +111,7 @@ suite('Decoration Render Options', () => { // URI, only minimal encoding s.registerDecorationType('test', 'example', { gutterIconPath: URI.parse('data:image/svg+xml;base64,PHN2ZyB4b+') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('data:image/svg+xml;base64,PHN2ZyB4b+') center center no-repeat;}`) > 0); + assert(readStyleSheet(styleSheet).indexOf(`{background:url('${CSS.escape('data:image/svg+xml;base64,PHN2ZyB4b+')}') center center no-repeat;}`) > 0); s.removeDecorationType('example'); function assertBackground(url1: string, url2: string) { @@ -130,22 +130,22 @@ suite('Decoration Render Options', () => { // single quote must always be escaped/encoded s.registerDecorationType('test', 'example', { gutterIconPath: URI.file('c:\\files\\foo\\b\'ar.png') }); - assertBackground('file:///c:/files/foo/b\\000027ar.png', 'vscode-file://vscode-app/c:/files/foo/b\\000027ar.png'); + assertBackground(CSS.escape('file:///c:/files/foo/b\'ar.png'), CSS.escape('vscode-file://vscode-app/c:/files/foo/b\'ar.png')); s.removeDecorationType('example'); } else { // unix file path (used as string) s.registerDecorationType('test', 'example', { gutterIconPath: URI.file('/Users/foo/bar.png') }); - assertBackground('file:///Users/foo/bar.png', 'vscode-file://vscode-app/Users/foo/bar.png'); + assertBackground(CSS.escape('file:///Users/foo/bar.png'), CSS.escape('vscode-file://vscode-app/Users/foo/bar.png')); s.removeDecorationType('example'); // single quote must always be escaped/encoded s.registerDecorationType('test', 'example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') }); - assertBackground('file:///Users/foo/b\\000027ar.png', 'vscode-file://vscode-app/Users/foo/b\\000027ar.png'); + assertBackground(CSS.escape('file:///Users/foo/b\'ar.png'), CSS.escape('vscode-file://vscode-app/Users/foo/b\'ar.png')); s.removeDecorationType('example'); } s.registerDecorationType('test', 'example', { gutterIconPath: URI.parse('http://test/pa\'th') }); - assert(readStyleSheet(styleSheet).indexOf(`{background:url('http://test/pa\\000027th') center center no-repeat;}`) > 0); + assert(readStyleSheet(styleSheet).indexOf(`{background:url('${CSS.escape('http://test/pa\'th')}') center center no-repeat;}`) > 0); s.removeDecorationType('example'); }); }); From 95386dec787946547812c7ee9d38cba19d6a6398 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 16 Dec 2024 19:27:10 -0600 Subject: [PATCH 288/479] make screen reader notification more descriptive, useful (#236295) --- .../accessibility/browser/accessibilityStatus.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityStatus.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityStatus.ts index 50ae856a62f..aa09758f5b4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityStatus.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityStatus.ts @@ -13,6 +13,7 @@ import { ConfigurationTarget, IConfigurationService } from '../../../../platform import { INotificationHandle, INotificationService, NotificationPriority } from '../../../../platform/notification/common/notification.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; +import { IOpenerService } from '../../../../platform/opener/common/opener.js'; export class AccessibilityStatus extends Disposable implements IWorkbenchContribution { @@ -26,7 +27,8 @@ export class AccessibilityStatus extends Disposable implements IWorkbenchContrib @IConfigurationService private readonly configurationService: IConfigurationService, @INotificationService private readonly notificationService: INotificationService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @IStatusbarService private readonly statusbarService: IStatusbarService + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IOpenerService private readonly openerService: IOpenerService, ) { super(); @@ -50,7 +52,7 @@ export class AccessibilityStatus extends Disposable implements IWorkbenchContrib private showScreenReaderNotification(): void { this.screenReaderNotification = this.notificationService.prompt( Severity.Info, - localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate VS Code?"), + localize('screenReaderDetectedExplanation.question', "Screen reader usage detected. Do you want to enable {0} to optimize the editor for screen reader usage?", 'editor.accessibilitySupport'), [{ label: localize('screenReaderDetectedExplanation.answerYes', "Yes"), run: () => { @@ -61,6 +63,12 @@ export class AccessibilityStatus extends Disposable implements IWorkbenchContrib run: () => { this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER); } + }, + { + label: localize('screenReaderDetectedExplanation.answerLearnMore', "Learn More"), + run: () => { + this.openerService.open('https://code.visualstudio.com/docs/editor/accessibility#_screen-readers'); + } }], { sticky: true, From ca3ff9f56444da1bfa4d942e7fea9154deeffbf9 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:26:30 +0100 Subject: [PATCH 289/479] SCM - quick diff should better handle untracked files (#236315) --- extensions/git/src/repository.ts | 8 +++++++- src/vs/workbench/contrib/scm/browser/quickDiffModel.ts | 4 ---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 73a54cef4af..21dd3b5819f 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1035,7 +1035,13 @@ export class Repository implements Disposable { } // Ignore path that is inside a merge group - if (this.mergeGroup.resourceStates.some(r => r.resourceUri.path === uri.path)) { + if (this.mergeGroup.resourceStates.some(r => pathEquals(r.resourceUri.fsPath, uri.fsPath))) { + return undefined; + } + + // Ignore path that is untracked + if (this.untrackedGroup.resourceStates.some(r => pathEquals(r.resourceUri.path, uri.path)) || + this.workingTreeGroup.resourceStates.some(r => pathEquals(r.resourceUri.path, uri.path) && r.type === Status.UNTRACKED)) { return undefined; } diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index 3d1ec39c3f6..bf9494da91c 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -214,10 +214,6 @@ export class QuickDiffModel extends Disposable { return; // disposed } - if (editorModels.every(editorModel => editorModel.textEditorModel.getValueLength() === 0)) { - result.changes = []; - } - this.setChanges(result.changes, result.mapChanges); }) .catch(err => onUnexpectedError(err)); From a5a6d6d21e8b03bf81105101125571db01ff8f23 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 17 Dec 2024 10:02:16 +0100 Subject: [PATCH 290/479] Improve wording and flow of restart EH blocker (fix #233912) (#236311) --- .../api/browser/mainThreadCustomEditors.ts | 2 +- .../common/customTextEditorModel.ts | 2 +- .../contrib/debug/browser/debugService.ts | 2 +- ...mentWorkspaceTrustTransitionParticipant.ts | 2 +- .../browser/extensionsWorkbenchService.ts | 2 +- .../notebook/common/notebookEditorInput.ts | 4 +-- .../browser/relauncher.contribution.ts | 2 +- .../common/abstractExtensionService.ts | 29 +++++++++++++++---- .../nativeExtensionService.ts | 2 +- .../browser/userDataProfileManagement.ts | 2 +- .../workspaceEditingService.ts | 2 +- 11 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts index 4fb33a99f16..cd15f673c29 100644 --- a/src/vs/workbench/api/browser/mainThreadCustomEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadCustomEditors.ts @@ -389,7 +389,7 @@ class MainThreadCustomEditorModel extends ResourceWorkingCopy implements ICustom this._register(workingCopyService.registerWorkingCopy(this)); this._register(extensionService.onWillStop(e => { - e.veto(true, localize('vetoExtHostRestart', "A custom editor for '{0}' is open.", this.name)); + e.veto(true, localize('vetoExtHostRestart', "An extension provided editor for '{0}' is still open that would close otherwise.", this.name)); })); } diff --git a/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts index 0ca157683f9..38648ff3dfd 100644 --- a/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts @@ -66,7 +66,7 @@ export class CustomTextEditorModel extends Disposable implements ICustomEditorMo })); this._register(extensionService.onWillStop(e => { - e.veto(true, localize('vetoExtHostRestart', "A custom text editor for '{0}' is open.", this.resource.path)); + e.veto(true, localize('vetoExtHostRestart', "An extension provided text editor for '{0}' is still open that would close otherwise.", this.name)); })); } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 7019624359c..1527a4c2101 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -207,7 +207,7 @@ export class DebugService implements IDebugService { this.disposables.add(extensionService.onWillStop(evt => { evt.veto( this.model.getSessions().length > 0, - nls.localize('active debug session', 'A debug session is still running.'), + nls.localize('active debug session', 'A debug session is still running that would terminate.'), ); })); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant.ts b/src/vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant.ts index 55de158e6b8..47065f253b4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant.ts @@ -39,7 +39,7 @@ export class ExtensionEnablementWorkspaceTrustTransitionParticipant extends Disp if (environmentService.remoteAuthority) { hostService.reload(); } else { - const stopped = await extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Restarting extension host due to workspace trust change.")); + const stopped = await extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Changing workspace trust")); await extensionEnablementService.updateExtensionsEnablementsWhenWorkspaceTrustChanges(); if (stopped) { extensionService.startExtensionHosts(); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 1a28f0027ee..ae7df7d09b3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1585,7 +1585,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } if (toAdd.length || toRemove.length) { - if (await this.extensionService.stopExtensionHosts(nls.localize('restart', "Enable or Disable extensions"), auto)) { + if (await this.extensionService.stopExtensionHosts(nls.localize('restart', "Changing extension enablement"), auto)) { await this.extensionService.startExtensionHosts({ toAdd, toRemove }); if (auto) { this.notificationService.notify({ diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index 92c066a28e7..f6c4b05d618 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -91,8 +91,8 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { } const reason = e.auto - ? localize('vetoAutoExtHostRestart', "One of the opened editors is a notebook editor.") - : localize('vetoExtHostRestart', "Notebook '{0}' could not be saved.", this.resource.path); + ? localize('vetoAutoExtHostRestart', "An extension provided notebook for '{0}' is still open that would close otherwise.", this.getName()) + : localize('vetoExtHostRestart', "An extension provided notebook for '{0}' could not be saved.", this.getName()); e.veto((async () => { const editors = editorService.findEditors(this); diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index 3cb188be749..54b2cff5b80 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -224,7 +224,7 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor if (environmentService.remoteAuthority) { hostService.reload(); // TODO@aeschli, workaround } else if (isNative) { - const stopped = await extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Restarting extension host due to a workspace folder change.")); + const stopped = await extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Changing workspace folders")); if (stopped) { extensionService.startExtensionHosts(); } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 68b71d7ce1b..8e63847fb9b 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -761,12 +761,29 @@ export abstract class AbstractExtensionService extends Disposable implements IEx const vetoReasonsArray = Array.from(vetoReasons); this._logService.warn(`Extension host was not stopped because of veto (stop reason: ${reason}, veto reason: ${vetoReasonsArray.join(', ')})`); - await this._dialogService.warn( - nls.localize('extensionStopVetoMessage', "The following operation was blocked: {0}", reason), - vetoReasonsArray.length === 1 ? - nls.localize('extensionStopVetoDetailsOne', "The reason for blocking the operation: {0}", vetoReasonsArray[0]) : - nls.localize('extensionStopVetoDetailsMany', "The reasons for blocking the operation:\n- {0}", vetoReasonsArray.join('\n -')), - ); + + let overrideVeto = false; + await this._dialogService.prompt({ + type: Severity.Warning, + message: nls.localize('extensionStopVetoMessage', "Restart of extensions was prevented but is required for: {0}. Do you want to proceed anyways?", reason), + detail: vetoReasonsArray.length === 1 ? + nls.localize('extensionStopVetoDetailsOne', "Reason: {0}", vetoReasonsArray[0]) : + nls.localize('extensionStopVetoDetailsMany', "Reasons:\n- {0}", vetoReasonsArray.join('\n -')), + buttons: [ + { + label: nls.localize('ok', "OK"), + run: () => { /* noop */ } + }, + { + label: nls.localize('proceedAnyways', "Proceed Anyways"), + run: () => overrideVeto = true + } + ] + }); + + if (overrideVeto) { + return true; + } } } diff --git a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts index 8adc7c19fe7..33eabcc36dd 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/nativeExtensionService.ts @@ -742,7 +742,7 @@ class RestartExtensionHostAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const extensionService = accessor.get(IExtensionService); - const stopped = await extensionService.stopExtensionHosts(nls.localize('restartExtensionHost.reason', "Restarting extension host on explicit request.")); + const stopped = await extensionService.stopExtensionHosts(nls.localize('restartExtensionHost.reason', "An explicit request")); if (stopped) { extensionService.startExtensionHosts(); } diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts index 88b46666077..e4b29d57e53 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -198,7 +198,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse if (shouldRestartExtensionHosts) { if (!isRemoteWindow) { - if (!(await this.extensionService.stopExtensionHosts(localize('switch profile', "Switching to a profile.")))) { + if (!(await this.extensionService.stopExtensionHosts(localize('switch profile', "Switching to a profile")))) { // If extension host did not stop, do not switch profile if (this.userDataProfilesService.profiles.some(p => p.id === this.userDataProfileService.currentProfile.id)) { await this.userDataProfilesService.setProfileForWorkspace(toWorkspaceIdentifier(this.workspaceContextService.getWorkspace()), this.userDataProfileService.currentProfile); diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index 625f69f60b9..c285804f40a 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -174,7 +174,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi } async enterWorkspace(workspaceUri: URI): Promise { - const stopped = await this.extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Opening a multi-root workspace.")); + const stopped = await this.extensionService.stopExtensionHosts(localize('restartExtensionHost.reason', "Opening a multi-root workspace")); if (!stopped) { return; } From 9963eb06d9ead53d8f42d73959b47f38554d21bd Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 17 Dec 2024 10:08:35 +0100 Subject: [PATCH 291/479] Removing showing of hover on modifier key press (#236317) removing showing of hover on modifier key press --- .../hover/browser/contentHoverController.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index e44c40a947d..a3f67bb85f6 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -5,7 +5,6 @@ import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, SHOW_OR_FOCUS_HOVER_ACTION_ID } from './hoverActionIds.js'; import { IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent } from '../../../browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../common/config/editorOptions.js'; @@ -243,13 +242,6 @@ export class ContentHoverController extends Disposable implements IEditorContrib if (isPotentialKeyboardShortcut) { return; } - const isModifierKeyPressed = this._isModifierKeyPressed(e); - if (isModifierKeyPressed && this._mouseMoveEvent) { - const contentWidget: ContentHoverWidgetWrapper = this._getOrCreateContentWidget(); - if (contentWidget.showsOrWillShow(this._mouseMoveEvent)) { - return; - } - } this.hideContentHover(); } @@ -267,13 +259,6 @@ export class ContentHoverController extends Disposable implements IEditorContrib return moreChordsAreNeeded || isHoverAction; } - private _isModifierKeyPressed(e: IKeyboardEvent): boolean { - return e.keyCode === KeyCode.Ctrl - || e.keyCode === KeyCode.Alt - || e.keyCode === KeyCode.Meta - || e.keyCode === KeyCode.Shift; - } - public hideContentHover(): void { if (_sticky) { return; From f5e5ee4b29158b0294783dba7dd3f3b92d6e9180 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:14:27 +0100 Subject: [PATCH 292/479] SCM - remove more debt from the quick diff (#236318) --- extensions/git/src/repository.ts | 17 +++++++++++++++++ .../contrib/scm/browser/quickDiffModel.ts | 10 +--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 21dd3b5819f..ac2649242f2 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1031,17 +1031,34 @@ export class Repository implements Disposable { // Ignore path that is not inside the current repository if (this.repositoryResolver.getRepository(uri) !== this) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is not part of the repository: ${uri.toString()}`); return undefined; } // Ignore path that is inside a merge group if (this.mergeGroup.resourceStates.some(r => pathEquals(r.resourceUri.fsPath, uri.fsPath))) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is part of a merge group: ${uri.toString()}`); return undefined; } // Ignore path that is untracked if (this.untrackedGroup.resourceStates.some(r => pathEquals(r.resourceUri.path, uri.path)) || this.workingTreeGroup.resourceStates.some(r => pathEquals(r.resourceUri.path, uri.path) && r.type === Status.UNTRACKED)) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is untracked: ${uri.toString()}`); + return undefined; + } + + const activeTabInput = window.tabGroups.activeTabGroup.activeTab?.input; + + // Ignore file that is on the right-hand side of a diff editor + if (activeTabInput instanceof TabInputTextDiff && pathEquals(activeTabInput.modified.fsPath, uri.fsPath)) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is on the right-hand side of a diff editor: ${uri.toString()}`); + return undefined; + } + + // Ignore file that is on the right -hand side of a multi-file diff editor + if (activeTabInput instanceof TabInputTextMultiDiff && activeTabInput.textDiffs.some(diff => pathEquals(diff.modified.fsPath, uri.fsPath))) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is on the right-hand side of a multi-file diff editor: ${uri.toString()}`); return undefined; } diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index bf9494da91c..c8a3d8b07dd 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -351,15 +351,7 @@ export class QuickDiffModel extends Disposable { } const isSynchronized = this._model.textEditorModel ? shouldSynchronizeModel(this._model.textEditorModel) : undefined; - const quickDiffs = await this.quickDiffService.getQuickDiffs(uri, this._model.getLanguageId(), isSynchronized); - - // TODO@lszomoru - find a long term solution for this - // When the QuickDiffModel is created for a diff editor, there is no - // need to compute the diff information for the `isSCM` quick diff - // provider as that information will be provided by the diff editor - return this.options.maxComputationTimeMs === undefined - ? quickDiffs.filter(quickDiff => !quickDiff.isSCM) - : quickDiffs; + return this.quickDiffService.getQuickDiffs(uri, this._model.getLanguageId(), isSynchronized); } findNextClosestChange(lineNumber: number, inclusive = true, provider?: string): number { From f283262be0d06d0c6759e52c1c47fbde06e2b172 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 17 Dec 2024 10:17:50 +0100 Subject: [PATCH 293/479] [json] `Unable to load schema, EISDIR: illegal operation on a directory (#236319) --- .../server/src/node/jsonServerMain.ts | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/extensions/json-language-features/server/src/node/jsonServerMain.ts b/extensions/json-language-features/server/src/node/jsonServerMain.ts index ad1ae439e59..c22cd14834d 100644 --- a/extensions/json-language-features/server/src/node/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/node/jsonServerMain.ts @@ -8,8 +8,8 @@ import { formatError } from '../utils/runner'; import { RequestService, RuntimeEnvironment, startServer } from '../jsonServer'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; -import { URI as Uri } from 'vscode-uri'; -import * as fs from 'fs'; +import { promises as fs } from 'fs'; +import * as l10n from '@vscode/l10n'; // Create a connection for the server. const connection: Connection = createConnection(); @@ -36,16 +36,17 @@ function getHTTPRequestService(): RequestService { function getFileRequestService(): RequestService { return { - getContent(location: string, encoding?: BufferEncoding) { - return new Promise((c, e) => { - const uri = Uri.parse(location); - fs.readFile(uri.fsPath, encoding, (err, buf) => { - if (err) { - return e(err); - } - c(buf.toString()); - }); - }); + async getContent(location: string, encoding?: BufferEncoding) { + try { + return (await fs.readFile(location, encoding)).toString(); + } catch (e) { + if (e.code === 'ENOENT') { + throw new Error(l10n.t('Schema not found: {0}', location)); + } else if (e.code === 'EISDIR') { + throw new Error(l10n.t('{0} is a directory, not a file', location)); + } + throw e; + } } }; } From 848e5a3495a9af7dc8523a25e12b81b267b2a4de Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 17 Dec 2024 11:09:44 +0100 Subject: [PATCH 294/479] Do not inherit outer editor's rulers (#236324) --- .../inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 481af0ed288..b2cd7d9ef56 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -247,6 +247,7 @@ export class InlineEditsSideBySideDiff extends Disposable { bracketPairsHorizontal: false, highlightActiveIndentation: false, }, + rulers: [], padding: { top: 0, bottom: 0 }, folding: false, selectOnLineNumbers: false, From b426e026e1640a08b827e3b28274c0bea654ccf8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 17 Dec 2024 11:26:12 +0100 Subject: [PATCH 295/479] Show remote name for files in recently opened (fix #143096) (#236309) --- src/vs/platform/label/common/label.ts | 3 ++- src/vs/workbench/browser/actions/windowActions.ts | 2 +- .../workbench/browser/parts/titlebar/menubarControl.ts | 2 +- .../contrib/search/browser/anythingQuickAccess.ts | 2 -- .../dialogs/browser/abstractFileDialogService.ts | 2 +- .../services/host/browser/browserHostService.ts | 2 +- .../host/electron-sandbox/nativeHostService.ts | 2 +- src/vs/workbench/services/label/common/labelService.ts | 10 +++++++--- 8 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index e920d9a1c21..c8d520ae357 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -20,8 +20,9 @@ export interface ILabelService { * If `relative` is passed returns a label relative to the workspace root that the uri belongs to. * If `noPrefix` is passed does not tildify the label and also does not prepand the root name for relative labels in a multi root scenario. * If `separator` is passed, will use that over the defined path separator of the formatter. + * If `appendWorkspaceSuffix` is passed, will append the name of the workspace to the label. */ - getUriLabel(resource: URI, options?: { relative?: boolean; noPrefix?: boolean; separator?: '/' | '\\' }): string; + getUriLabel(resource: URI, options?: { relative?: boolean; noPrefix?: boolean; separator?: '/' | '\\'; appendWorkspaceSuffix?: boolean }): string; getUriBasenameLabel(resource: URI): string; getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | IWorkspace), options?: { verbose: Verbosity }): string; getHostLabel(scheme: string, authority?: string): string; diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index e2248c750fb..2fb9c8edbb3 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -211,7 +211,7 @@ abstract class BaseOpenRecentAction extends Action2 { resource = recent.fileUri; iconClasses = getIconClasses(modelService, languageService, resource, FileKind.FILE); openable = { fileUri: resource }; - fullLabel = recent.label || labelService.getUriLabel(resource); + fullLabel = recent.label || labelService.getUriLabel(resource, { appendWorkspaceSuffix: true }); } const { name, parentPath } = splitRecentLabel(fullLabel); diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index ac4940f1c28..50e68318650 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -327,7 +327,7 @@ export abstract class MenubarControl extends Disposable { openable = { workspaceUri: uri }; } else { uri = recent.fileUri; - label = recent.label || this.labelService.getUriLabel(uri); + label = recent.label || this.labelService.getUriLabel(uri, { appendWorkspaceSuffix: true }); commandId = 'openRecentFile'; openable = { fileUri: uri }; } diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index c08b3518cf7..bcb65cdddfd 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -484,8 +484,6 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider = []; for (const editor of this.historyService.getHistory()) { const resource = editor.resource; - // allow untitled and terminal editors to go through - // allow github copilot chat to go through if (!resource) { continue; } diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index c7e890fce1d..7223babe5ef 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -230,7 +230,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService { } protected addFileToRecentlyOpened(uri: URI): void { - this.workspacesService.addRecentlyOpened([{ fileUri: uri, label: this.labelService.getUriLabel(uri) }]); + this.workspacesService.addRecentlyOpened([{ fileUri: uri, label: this.labelService.getUriLabel(uri, { appendWorkspaceSuffix: true }) }]); } protected async pickFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions): Promise { diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index c2a2374c6c9..afd2a3d1267 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -428,7 +428,7 @@ export class BrowserHostService extends Disposable implements IHostService { return this.labelService.getWorkspaceLabel(getWorkspaceIdentifier(openable.workspaceUri), { verbose: Verbosity.LONG }); } - return this.labelService.getUriLabel(openable.fileUri); + return this.labelService.getUriLabel(openable.fileUri, { appendWorkspaceSuffix: true }); } private shouldReuse(options: IOpenWindowOptions = Object.create(null), isFile: boolean): boolean { diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index 8bdfe9743fe..be5cdaa8bb6 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -130,7 +130,7 @@ class WorkbenchHostService extends Disposable implements IHostService { return this.labelService.getWorkspaceLabel({ id: '', configPath: openable.workspaceUri }, { verbose: Verbosity.LONG }); } - return this.labelService.getUriLabel(openable.fileUri); + return this.labelService.getUriLabel(openable.fileUri, { appendWorkspaceSuffix: true }); } private doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise { diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 941b49db19a..d99ee6ad9b4 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -208,19 +208,23 @@ export class LabelService extends Disposable implements ILabelService { return bestResult ? bestResult.formatting : undefined; } - getUriLabel(resource: URI, options: { relative?: boolean; noPrefix?: boolean; separator?: '/' | '\\' } = {}): string { + getUriLabel(resource: URI, options: { relative?: boolean; noPrefix?: boolean; separator?: '/' | '\\'; appendWorkspaceSuffix?: boolean } = {}): string { let formatting = this.findFormatting(resource); if (formatting && options.separator) { // mixin separator if defined from the outside formatting = { ...formatting, separator: options.separator }; } - const label = this.doGetUriLabel(resource, formatting, options); + let label = this.doGetUriLabel(resource, formatting, options); // Without formatting we still need to support the separator // as provided in options (https://github.com/microsoft/vscode/issues/130019) if (!formatting && options.separator) { - return label.replace(sepRegexp, options.separator); + label = label.replace(sepRegexp, options.separator); + } + + if (options.appendWorkspaceSuffix && formatting?.workspaceSuffix) { + label = this.appendWorkspaceSuffix(label, resource); } return label; From f78af5335027855ca7221e4211978034b858c612 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 17 Dec 2024 11:38:12 +0100 Subject: [PATCH 296/479] Fix extra primary button in comments (#236325) Fixes https://github.com/microsoft/vscode-pull-request-github/issues/6553 --- src/vs/workbench/contrib/comments/browser/commentNode.ts | 2 +- src/vs/workbench/contrib/comments/browser/commentReply.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 37958bce7d8..f90a81288a3 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -658,7 +658,7 @@ export class CommentNode extends Disposable { this._register(menu); this._register(menu.onDidChange(() => { - this._commentEditorActions?.setActions(menu); + this._commentEditorActions?.setActions(menu, true); })); this._commentEditorActions = new CommentFormActions(this.keybindingService, this._contextKeyService, this.contextMenuService, container, (action: IAction): void => { diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index 981a3513d59..37bb741dc68 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -300,7 +300,7 @@ export class CommentReply extends Disposable { const editorMenu = this._commentMenus.getCommentEditorActions(this._contextKeyService); this._register(editorMenu); this._register(editorMenu.onDidChange(() => { - this._commentEditorActions.setActions(editorMenu); + this._commentEditorActions.setActions(editorMenu, true); })); this._commentEditorActions = new CommentFormActions(this.keybindingService, this._contextKeyService, this.contextMenuService, container, async (action: IAction) => { From 9b0cd6551590014e8b91e78425848ccb6a12c6e6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 17 Dec 2024 12:22:51 +0100 Subject: [PATCH 297/479] theme - fix some colors in status bar for #235718 (#236331) --- extensions/theme-defaults/themes/hc_light.json | 4 +++- extensions/theme-defaults/themes/light_modern.json | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/theme-defaults/themes/hc_light.json b/extensions/theme-defaults/themes/hc_light.json index 6d192e76757..e59494f9cfb 100644 --- a/extensions/theme-defaults/themes/hc_light.json +++ b/extensions/theme-defaults/themes/hc_light.json @@ -569,6 +569,8 @@ } ], "colors": { - "actionBar.toggledBackground": "#dddddd" + "actionBar.toggledBackground": "#dddddd", + "statusBarItem.remoteBackground": "#FFFFFF", + "statusBarItem.remoteForeground": "#000000" } } diff --git a/extensions/theme-defaults/themes/light_modern.json b/extensions/theme-defaults/themes/light_modern.json index 9b4d627fcd1..7f5e622639c 100644 --- a/extensions/theme-defaults/themes/light_modern.json +++ b/extensions/theme-defaults/themes/light_modern.json @@ -104,6 +104,8 @@ "statusBar.background": "#F8F8F8", "statusBar.foreground": "#3B3B3B", "statusBar.border": "#E5E5E5", + "statusBarItem.hoverBackground": "#B8B8B850", + "statusBarItem.compactHoverBackground": "#CCCCCC", "statusBar.debuggingBackground": "#FD716C", "statusBar.debuggingForeground": "#000000", "statusBar.focusBorder": "#005FB8", From c269884f97e3ec4e62495ce64dec99d2d1f9db0b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 17 Dec 2024 12:30:48 +0100 Subject: [PATCH 298/479] disable sticky scroll while steaming edits (#236332) disable sticky scroll white steaming edits fixes https://github.com/microsoft/vscode-copilot/issues/11102 --- .../contrib/chat/browser/chatEditorController.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts index 18a7c1a258e..e2a186b5886 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorController.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorController.ts @@ -11,7 +11,7 @@ import { themeColorFromId } from '../../../../base/common/themables.js'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IOverlayWidgetPositionCoordinates, IViewZone, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; import { LineSource, renderLines, RenderOptions } from '../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { diffAddDecoration, diffDeleteDecoration, diffWholeLineAddDecoration } from '../../../../editor/browser/widget/diffEditor/registrations.contribution.js'; -import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; +import { EditorOption, IEditorStickyScrollOptions } from '../../../../editor/common/config/editorOptions.js'; import { EditOperation, ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; import { Range } from '../../../../editor/common/core/range.js'; import { IDocumentDiff } from '../../../../editor/common/diff/documentDiffProvider.js'; @@ -164,25 +164,30 @@ export class ChatEditorController extends Disposable implements IEditorContribut let actualReadonly: boolean | undefined; let actualDeco: 'off' | 'editable' | 'on' | undefined; + let actualStickyScroll: IEditorStickyScrollOptions | undefined; this._register(autorun(r => { const value = shouldBeReadOnly.read(r); if (value) { actualReadonly ??= this._editor.getOption(EditorOption.readOnly); actualDeco ??= this._editor.getOption(EditorOption.renderValidationDecorations); + actualStickyScroll ??= this._editor.getOption(EditorOption.stickyScroll); this._editor.updateOptions({ readOnly: true, - renderValidationDecorations: 'off' + renderValidationDecorations: 'off', + stickyScroll: { enabled: false } }); } else { - if (actualReadonly !== undefined && actualDeco !== undefined) { + if (actualReadonly !== undefined && actualDeco !== undefined && actualStickyScroll !== undefined) { this._editor.updateOptions({ readOnly: actualReadonly, - renderValidationDecorations: actualDeco + renderValidationDecorations: actualDeco, + stickyScroll: actualStickyScroll }); actualReadonly = undefined; actualDeco = undefined; + actualStickyScroll = undefined; } } })); From f776d0611ec9fc8c8169dc6883ab1adaa7965f24 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Dec 2024 04:09:17 -0800 Subject: [PATCH 299/479] Fill pwsh commands via Get-Command Fixes #235024 --- .../src/terminalSuggestMain.ts | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 0c29d9688b1..5071c4c79ce 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -25,40 +25,44 @@ function getBuiltinCommands(shell: string): string[] | undefined { } const filter = (cmd: string) => cmd; const options: ExecOptionsWithStringEncoding = { encoding: 'utf-8', shell }; + let commands: string[] | undefined; switch (shellType) { case 'bash': { const bashOutput = execSync('compgen -b', options); - const bashResult = bashOutput.split('\n').filter(filter); - if (bashResult.length) { - cachedBuiltinCommands?.set(shellType, bashResult); - return bashResult; - } + commands = bashOutput.split('\n').filter(filter); break; } case 'zsh': { const zshOutput = execSync('printf "%s\\n" ${(k)builtins}', options); - const zshResult = zshOutput.split('\n').filter(filter); - if (zshResult.length) { - cachedBuiltinCommands?.set(shellType, zshResult); - return zshResult; - } + commands = zshOutput.split('\n').filter(filter); + break; } case 'fish': { // TODO: ghost text in the command line prevents // completions from working ATM for fish const fishOutput = execSync('functions -n', options); - const fishResult = fishOutput.split(', ').filter(filter); - if (fishResult.length) { - cachedBuiltinCommands?.set(shellType, fishResult); - return fishResult; - } + commands = fishOutput.split(', ').filter(filter); break; } case 'pwsh': { - // native pwsh completions are builtin to vscode - return []; + const output = execSync('Get-Command | Select-Object Name, CommandType, DisplayName | ConvertTo-Json', options); + let json: any; + try { + json = JSON.parse(output); + } catch (e) { + console.error('Error parsing pwsh output:', e); + return []; + } + // TODO: Return a rich type with kind and detail + commands = (json as any[]).map(e => e.Name); + break; } } + // TODO: Cache failure results too + if (commands?.length) { + cachedBuiltinCommands?.set(shellType, commands); + return commands; + } return; } catch (error) { From cdd41b08dba59afbff4a499f522e35dde0d53caa Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 17 Dec 2024 04:27:47 -0800 Subject: [PATCH 300/479] Reduce duplication in terminal completion service tests --- .../browser/terminalCompletionService.test.ts | 94 ++++++------------- 1 file changed, 28 insertions(+), 66 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts index 3ce17efc700..1ce22b4138c 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts @@ -6,7 +6,6 @@ import { URI } from '../../../../../../base/common/uri.js'; import { IFileService, IFileStatWithMetadata, IResolveMetadataFileOptions } from '../../../../../../platform/files/common/files.js'; import { TerminalCompletionService, TerminalCompletionItemKind, TerminalResourceRequestConfig } from '../../browser/terminalCompletionService.js'; -import sinon from 'sinon'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; import assert from 'assert'; import { isWindows } from '../../../../../../base/common/platform.js'; @@ -44,10 +43,6 @@ suite('TerminalCompletionService', () => { childResources = []; }); - teardown(() => { - sinon.restore(); - }); - suite('resolveResources should return undefined', () => { test('if cwd is not provided', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { @@ -69,157 +64,124 @@ suite('TerminalCompletionService', () => { }); suite('resolveResources should return folder completions', () => { - test('', async () => { + setup(() => { + validResources = [URI.parse('file:///test')]; + const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; + const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; + childResources = [childFolder, childFile]; + }); + + test('|', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), foldersRequested: true, pathSeparator }; - validResources = [URI.parse('file:///test')]; - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; - childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '', 1); - assert(!!result); - assert(result.length === 1); - assert.deepEqual(result![0], { + assert.deepEqual(result, [{ label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 1, replacementLength: 1 - }); + }]); }); - test('.', async () => { + test('.|', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), foldersRequested: true, pathSeparator }; - validResources = [URI.parse('file:///test')]; - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; - childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '.', 2); - assert(!!result); - assert(result.length === 1); - assert.deepEqual(result![0], { + assert.deepEqual(result, [{ label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 1, replacementLength: 1 - }); + }]); }); - test('./', async () => { + test('./|', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), foldersRequested: true, pathSeparator }; - validResources = [URI.parse('file:///test')]; - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; - childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './', 3); - assert(!!result); - assert(result.length === 1); - assert.deepEqual(result![0], { + assert.deepEqual(result, [{ label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 1, replacementLength: 2 - }); + }]); }); - test('cd ', async () => { + test('cd |', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), foldersRequested: true, pathSeparator }; - validResources = [URI.parse('file:///test')]; - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; - childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ', 3); - assert(!!result); - assert(result.length === 1); - assert.deepEqual(result![0], { + assert.deepEqual(result, [{ label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 3, replacementLength: 3 - }); + }]); }); - test('cd .', async () => { + test('cd .|', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), foldersRequested: true, pathSeparator }; - validResources = [URI.parse('file:///test/')]; - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; - childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd .', 4); - assert(!!result); - assert(result.length === 1); - assert.deepEqual(result![0], { + assert.deepEqual(result, [{ label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 3, replacementLength: 1 // replacing . - }); + }]); }); - test('cd ./', async () => { + test('cd ./|', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), foldersRequested: true, pathSeparator }; - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; - childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ./', 5); - assert(!!result); - assert(result.length === 1); - assert.deepEqual(result![0], { + assert.deepEqual(result, [{ label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 3, replacementLength: 2 // replacing ./ - }); + }]); }); - test('cd ./f', async () => { + test('cd ./f|', async () => { const resourceRequestConfig: TerminalResourceRequestConfig = { cwd: URI.parse('file:///test'), foldersRequested: true, pathSeparator }; - const childFolder = { resource: URI.parse('file:///test/folder1/'), name: 'folder1', isDirectory: true, isFile: false }; - const childFile = { resource: URI.parse('file:///test/file1.txt'), name: 'file1.txt', isDirectory: false, isFile: true }; - childResources = [childFolder, childFile]; const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ./f', 6); - assert(!!result); - assert(result.length === 1); - assert.deepEqual(result![0], { + assert.deepEqual(result, [{ label: `.${pathSeparator}folder1${pathSeparator}`, kind: TerminalCompletionItemKind.Folder, isDirectory: true, isFile: false, replacementIndex: 3, replacementLength: 3 // replacing ./f - }); + }]); }); }); }); From 6997bee2acbcec80320a948d520e388f913f9d04 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:49:21 +0100 Subject: [PATCH 301/479] Make badge smaller, reduce margin and change color when icon (#236346) make badge smaller and closer to label --- .../browser/parts/media/paneCompositePart.css | 12 ++++++------ src/vs/workbench/browser/parts/panel/panelPart.ts | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index 2b4518ca204..de625937628 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -183,7 +183,7 @@ .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .badge, .monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .badge { - margin-left: 8px; + margin-left: 2px; display: flex; align-items: center; } @@ -196,11 +196,11 @@ .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .badge .badge-content, .monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .badge .badge-content { padding: 3px 5px; - border-radius: 11px; - font-size: 11px; - min-width: 18px; - height: 18px; - line-height: 11px; + border-radius: 10px; + font-size: 10px; + min-width: 16px; + height: 16px; + line-height: 10px; font-weight: normal; text-align: center; display: inline-block; diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 4646b4412f3..6363f266384 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -15,8 +15,8 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { TogglePanelAction } from './panelActions.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BORDER } from '../../../common/theme.js'; -import { contrastBorder, badgeBackground, badgeForeground } from '../../../../platform/theme/common/colorRegistry.js'; +import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND } from '../../../common/theme.js'; +import { badgeBackground, badgeForeground, contrastBorder } from '../../../../platform/theme/common/colorRegistry.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { Dimension } from '../../../../base/browser/dom.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; @@ -130,12 +130,13 @@ export class PanelPart extends AbstractPaneCompositePart { } protected getCompositeBarOptions(): IPaneCompositeBarOptions { + const showIcons = this.configurationService.getValue('workbench.panel.showLabels') === false; return { partContainerClass: 'panel', pinnedViewContainersKey: 'workbench.panel.pinnedPanels', placeholderViewContainersKey: 'workbench.panel.placeholderPanels', viewContainersWorkspaceStateKey: 'workbench.panel.viewContainersWorkspaceState', - icon: this.configurationService.getValue('workbench.panel.showLabels') === false, + icon: showIcons, orientation: ActionsOrientation.HORIZONTAL, recomputeSizes: true, activityHoverOptions: { @@ -152,8 +153,8 @@ export class PanelPart extends AbstractPaneCompositePart { activeBorderBottomColor: theme.getColor(PANEL_ACTIVE_TITLE_BORDER), activeForegroundColor: theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND), inactiveForegroundColor: theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND), - badgeBackground: theme.getColor(badgeBackground), - badgeForeground: theme.getColor(badgeForeground), + badgeBackground: theme.getColor(showIcons ? ACTIVITY_BAR_BADGE_BACKGROUND : badgeBackground), + badgeForeground: theme.getColor(showIcons ? ACTIVITY_BAR_BADGE_FOREGROUND : badgeForeground), dragAndDropBorder: theme.getColor(PANEL_DRAG_AND_DROP_BORDER) }) }; From d162ceb7fecc3476f27d75196e3dd71b06c51bc1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 17 Dec 2024 15:15:31 +0100 Subject: [PATCH 302/479] extension events use new `ExtensionError` so that these errors don't make it into "normal" error telemetry (#236336) * extension events use new `ExtensionError` so that these errors don't make it into "normal" error telemetry fixes https://github.com/microsoft/vscode/issues/232914 * fix tests --- .../platform/extensions/common/extensions.ts | 14 +++++++++++ .../workbench/api/common/extHost.api.impl.ts | 5 ++-- .../workbench/api/common/extensionHostMain.ts | 25 +++++++++++++------ .../api/test/common/extensionHostMain.test.ts | 7 ++++-- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index cee0043bad6..687fd50dc36 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -447,6 +447,20 @@ export class ExtensionIdentifierMap { } } +/** + * An error that is clearly from an extension, identified by the `ExtensionIdentifier` + */ +export class ExtensionError extends Error { + + readonly extension: ExtensionIdentifier; + + constructor(extensionIdentifier: ExtensionIdentifier, cause: Error, message?: string) { + super(`Error in extension ${ExtensionIdentifier.toKey(extensionIdentifier)}: ${message ?? cause.message}`, { cause }); + this.name = 'ExtensionError'; + this.extension = extensionIdentifier; + } +} + export interface IRelaxedExtensionDescription extends IRelaxedExtensionManifest { id?: string; identifier: ExtensionIdentifier; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 85ff1aa52b2..868906908ea 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -14,7 +14,7 @@ import { TextEditorCursorStyle } from '../../../editor/common/config/editorOptio import { score, targetsNotebooks } from '../../../editor/common/languageSelector.js'; import * as languageConfiguration from '../../../editor/common/languages/languageConfiguration.js'; import { OverviewRulerLane } from '../../../editor/common/model.js'; -import { ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; +import { ExtensionError, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import * as files from '../../../platform/files/common/files.js'; import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js'; import { ILogService, ILoggerService, LogLevel } from '../../../platform/log/common/log.js'; @@ -245,8 +245,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I try { listener.call(thisArgs, e); } catch (err) { - errors.onUnexpectedExternalError(new Error(`[ExtensionListenerError] Extension '${extension.identifier.value}' FAILED to handle event: ${err.toString()}`, { cause: err })); - extHostTelemetry.onExtensionError(extension.identifier, err); + errors.onUnexpectedExternalError(new ExtensionError(extension.identifier, err, 'FAILED to handle event')); } }); disposables?.push(handle); diff --git a/src/vs/workbench/api/common/extensionHostMain.ts b/src/vs/workbench/api/common/extensionHostMain.ts index 6c93b88e428..d0180c1a081 100644 --- a/src/vs/workbench/api/common/extensionHostMain.ts +++ b/src/vs/workbench/api/common/extensionHostMain.ts @@ -11,7 +11,7 @@ import { IMessagePassingProtocol } from '../../../base/parts/ipc/common/ipc.js'; import { MainContext, MainThreadConsoleShape } from './extHost.protocol.js'; import { IExtensionHostInitData } from '../../services/extensions/common/extensionHostProtocol.js'; import { RPCProtocol } from '../../services/extensions/common/rpcProtocol.js'; -import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; +import { ExtensionError, ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ILogService } from '../../../platform/log/common/log.js'; import { getSingletonServiceDescriptors } from '../../../platform/instantiation/common/extensions.js'; import { ServiceCollection } from '../../../platform/instantiation/common/serviceCollection.js'; @@ -119,15 +119,24 @@ export abstract class ErrorHandler { logService.error(err); const errorData = errors.transformErrorForSerialization(err); - const stackData = extensionErrors.get(err); - if (!stackData?.extensionIdentifier) { - mainThreadErrors.$onUnexpectedError(errorData); - return; + + let extension: ExtensionIdentifier | undefined; + if (err instanceof ExtensionError) { + extension = err.extension; + } else { + const stackData = extensionErrors.get(err); + extension = stackData?.extensionIdentifier; + } + + if (extension) { + mainThreadExtensions.$onExtensionRuntimeError(extension, errorData); + const reported = extensionTelemetry.onExtensionError(extension, err); + logService.trace('forwarded error to extension?', reported, extension); } + }); - mainThreadExtensions.$onExtensionRuntimeError(stackData.extensionIdentifier, errorData); - const reported = extensionTelemetry.onExtensionError(stackData.extensionIdentifier, err); - logService.trace('forwarded error to extension?', reported, stackData); + errors.errorHandler.addListener(err => { + mainThreadErrors.$onUnexpectedError(err); }); } } diff --git a/src/vs/workbench/api/test/common/extensionHostMain.test.ts b/src/vs/workbench/api/test/common/extensionHostMain.test.ts index cda646f07cf..aa96a67b445 100644 --- a/src/vs/workbench/api/test/common/extensionHostMain.test.ts +++ b/src/vs/workbench/api/test/common/extensionHostMain.test.ts @@ -14,7 +14,7 @@ import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform import { InstantiationService } from '../../../../platform/instantiation/common/instantiationService.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { ILogService, NullLogService } from '../../../../platform/log/common/log.js'; -import { MainThreadExtensionServiceShape } from '../../common/extHost.protocol.js'; +import { MainThreadErrorsShape, MainThreadExtensionServiceShape } from '../../common/extHost.protocol.js'; import { ExtensionPaths, IExtHostExtensionService } from '../../common/extHostExtensionService.js'; import { IExtHostRpcService } from '../../common/extHostRpcService.js'; import { IExtHostTelemetry } from '../../common/extHostTelemetry.js'; @@ -30,9 +30,12 @@ suite('ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slo } const extensionsIndex = TernarySearchTree.forUris(); - const mainThreadExtensionsService = new class extends mock() { + const mainThreadExtensionsService = new class extends mock() implements MainThreadErrorsShape { override $onExtensionRuntimeError(extensionId: ExtensionIdentifier, data: SerializedError): void { + } + $onUnexpectedError(err: any | SerializedError): void { + } }; From 206033fd3f1cabff14b46e3ffdb3e5c487dd05a3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 17 Dec 2024 15:36:20 +0100 Subject: [PATCH 303/479] drop code lenses with invalid/missing range (#236353) https://github.com/microsoft/vscode/issues/233159 --- src/vs/workbench/api/common/extHostLanguageFeatures.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index cd42bd3bf77..9783b16e880 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -130,6 +130,12 @@ class CodeLensAdapter { lenses: [], }; for (let i = 0; i < lenses.length; i++) { + + if (!Range.isRange(lenses[i].range)) { + console.warn('INVALID code lens, range is not defined', this._extension.identifier.value); + continue; + } + result.lenses.push({ cacheId: [cacheId, i], range: typeConvert.Range.from(lenses[i].range), From 317d55da7b91e07131403663d8aa6f9499e3a112 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 17 Dec 2024 16:08:13 +0100 Subject: [PATCH 304/479] Pressing alt/opt makes the hover temporarily sticky (#236356) * add alt key to make hover temporarily sticky * adding code --- .../hover/browser/contentHoverController.ts | 39 ++++++++++++++++--- .../browser/contentHoverWidgetWrapper.ts | 26 ++++++++++--- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index a3f67bb85f6..eec21e17259 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, SHOW_OR_FOCUS_HOVER_ACTION_ID } from './hoverActionIds.js'; -import { IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { IKeyboardEvent, StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent } from '../../../browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../common/config/editorOptions.js'; import { Range } from '../../../common/core/range.js'; @@ -22,6 +22,9 @@ import { ContentHoverWidgetWrapper } from './contentHoverWidgetWrapper.js'; import './hover.css'; import { Emitter } from '../../../../base/common/event.js'; import { isOnColorDecorator } from '../../colorPicker/browser/hoverColorPicker/hoverColorPicker.js'; +import { KeyCode } from '../../../../base/common/keyCodes.js'; +import { EventType } from '../../../../base/browser/dom.js'; +import { mainWindow } from '../../../../base/browser/window.js'; // sticky hover widget which doesn't disappear on focus out and such const _sticky = false @@ -92,11 +95,18 @@ export class ContentHoverController extends Disposable implements IEditorContrib this._listenersStore.add(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); this._listenersStore.add(this._editor.onMouseUp(() => this._onEditorMouseUp())); this._listenersStore.add(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); - this._listenersStore.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); this._listenersStore.add(this._editor.onMouseLeave((e) => this._onEditorMouseLeave(e))); this._listenersStore.add(this._editor.onDidChangeModel(() => this._cancelSchedulerAndHide())); this._listenersStore.add(this._editor.onDidChangeModelContent(() => this._cancelScheduler())); this._listenersStore.add(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e))); + const keyDownListener = (e: KeyboardEvent) => this._onKeyDown(e); + const keyUpListener = (e: KeyboardEvent) => this._onKeyUp(e); + mainWindow.addEventListener(EventType.KEY_DOWN, keyDownListener); + mainWindow.addEventListener(EventType.KEY_UP, keyUpListener); + this._listenersStore.add(toDisposable(() => { + mainWindow.removeEventListener(EventType.KEY_DOWN, keyDownListener); + mainWindow.removeEventListener(EventType.KEY_UP, keyUpListener); + })); } private _unhookListeners(): void { @@ -155,6 +165,9 @@ export class ContentHoverController extends Disposable implements IEditorContrib if (_sticky) { return; } + if (this._contentWidget) { + this._contentWidget.temporarilySticky = false; + } this.hideContentHover(); } @@ -234,17 +247,31 @@ export class ContentHoverController extends Disposable implements IEditorContrib this.hideContentHover(); } - private _onKeyDown(e: IKeyboardEvent): void { - if (!this._editor.hasModel()) { + private _onKeyDown(e: KeyboardEvent): void { + if (!this._contentWidget) { return; } - const isPotentialKeyboardShortcut = this._isPotentialKeyboardShortcut(e); + const event = new StandardKeyboardEvent(e); + if (event.keyCode === KeyCode.Alt) { + this._contentWidget.temporarilySticky = true; + } + const isPotentialKeyboardShortcut = this._isPotentialKeyboardShortcut(event); if (isPotentialKeyboardShortcut) { return; } this.hideContentHover(); } + private _onKeyUp(e: KeyboardEvent): void { + if (!this._contentWidget) { + return; + } + const event = new StandardKeyboardEvent(e); + if (event.keyCode === KeyCode.Alt) { + this._contentWidget.temporarilySticky = false; + } + } + private _isPotentialKeyboardShortcut(e: IKeyboardEvent): boolean { if (!this._editor.hasModel() || !this._contentWidget) { return false; diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts index ad9a9920967..228e9533d29 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts @@ -27,6 +27,7 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge private _currentResult: ContentHoverResult | null = null; private _renderedContentHover: RenderedContentHover | undefined; + private _temporarilySticky: boolean = false; private readonly _contentHoverWidget: ContentHoverWidget; private readonly _participants: IEditorHoverParticipant[]; @@ -162,11 +163,25 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge if (currentHoverResultIsEmpty) { currentHoverResult = null; } + const hoverVisible = this._contentHoverWidget.isVisible; + if (!hoverVisible) { + this._renderResult(currentHoverResult); + } else { + if (this._temporarilySticky) { + return; + } else { + this._renderResult(currentHoverResult); + } + } + } + + private _renderResult(currentHoverResult: ContentHoverResult | null): void { this._currentResult = currentHoverResult; if (this._currentResult) { this._showHover(this._currentResult); } else { - this._hideHover(); + this._contentHoverWidget.hide(); + this._participants.forEach(participant => participant.handleHide?.()); } } @@ -215,11 +230,6 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge } } - private _hideHover(): void { - this._contentHoverWidget.hide(); - this._participants.forEach(participant => participant.handleHide?.()); - } - private _getHoverContext(): IEditorHoverContext { const hide = () => { this.hide(); @@ -293,6 +303,10 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge } } + public set temporarilySticky(value: boolean) { + this._temporarilySticky = value; + } + public startShowingAtRange(range: Range, mode: HoverStartMode, source: HoverStartSource, focus: boolean): void { this._startShowingOrUpdateHover(new HoverRangeAnchor(0, range, undefined, undefined), mode, source, focus, null); } From 538412ba9def08421a42e7880bdbd31803dbb88c Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:15:26 +0100 Subject: [PATCH 305/479] Diff - manually revert 854108f7d2a8b40aa1ac0390f95088e61153ebc5 (#236355) --- .../diffEditor/components/diffEditorEditors.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts index 5d8f652a485..638c9fe8bd5 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts @@ -19,6 +19,7 @@ import { localize } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { DiffEditorOptions } from '../diffEditorOptions.js'; +import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; export class DiffEditorEditors extends Disposable { public readonly original = this._register(this._createLeftHandSideEditor(this._options.editorOptions.get(), this._argCodeEditorWidgetOptions.originalEditor || {})); @@ -51,6 +52,7 @@ export class DiffEditorEditors extends Disposable { private readonly _options: DiffEditorOptions, private _argCodeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, private readonly _createInnerEditor: (instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions) => CodeEditorWidget, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IKeybindingService private readonly _keybindingService: IKeybindingService ) { @@ -80,14 +82,22 @@ export class DiffEditorEditors extends Disposable { private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { const leftHandSideOptions = this._adjustOptionsForLeftHandSide(undefined, options); const editor = this._constructInnerEditor(this._instantiationService, this.originalEditorElement, leftHandSideOptions, codeEditorWidgetOptions); - editor.setContextValue('isInDiffLeftEditor', true); + + const isInDiffLeftEditorKey = this._contextKeyService.createKey('isInDiffLeftEditor', editor.hasWidgetFocus()); + this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); + return editor; } private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { const rightHandSideOptions = this._adjustOptionsForRightHandSide(undefined, options); const editor = this._constructInnerEditor(this._instantiationService, this.modifiedEditorElement, rightHandSideOptions, codeEditorWidgetOptions); - editor.setContextValue('isInDiffRightEditor', true); + + const isInDiffRightEditorKey = this._contextKeyService.createKey('isInDiffRightEditor', editor.hasWidgetFocus()); + this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); + return editor; } From 0acab1b9b56310c5578ef540a9028a53be98af09 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 17 Dec 2024 16:20:39 +0100 Subject: [PATCH 306/479] Fixes #11380 (#236352) Adjusts zIndex. Fixes microsoft/vscode-copilot#11380 --- .../browser/view/inlineEdits/sideBySideDiff.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index b2cd7d9ef56..d36ae86f085 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -601,7 +601,7 @@ export class InlineEditsSideBySideDiff extends Disposable { class: 'inline-edits-view', style: { overflow: 'visible', - zIndex: '100', + zIndex: '10', display: this._display, }, }, [ From f76df5bbe6c21bccd7a666043c05f68ac6d32dbd Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 17 Dec 2024 16:24:47 +0100 Subject: [PATCH 307/479] Fixes #11376 (#236358) --- .../browser/view/inlineEdits/sideBySideDiff.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index d36ae86f085..0df68982f14 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -412,7 +412,7 @@ export class InlineEditsSideBySideDiff extends Disposable { const codeEditDist = codeEditDistRange.clip(dist); const editHeight = this._editor.getOption(EditorOption.lineHeight) * inlineEdit.modifiedLineRange.length; - const previewEditorWidth = remainingWidthRightOfEditor + editorLayout.width - editorLayout.contentLeft - codeEditDist; + const previewEditorWidth = Math.min(previewContentWidth, remainingWidthRightOfEditor + editorLayout.width - editorLayout.contentLeft - codeEditDist); const edit1 = new Point(left + codeEditDist, selectionTop); const edit2 = new Point(left + codeEditDist, selectionTop + editHeight); From 92965da2b8f50ef4f3109ca2d1c58f26d7bff79c Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 17 Dec 2024 16:29:06 +0100 Subject: [PATCH 308/479] When tab is pressed when content widget is focused, do not hide the hover (#236360) when tab is pressed when content widget is focused, do not hide the hover --- src/vs/editor/contrib/hover/browser/contentHoverController.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index eec21e17259..09cb6efcab4 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -259,6 +259,9 @@ export class ContentHoverController extends Disposable implements IEditorContrib if (isPotentialKeyboardShortcut) { return; } + if (this._contentWidget.isFocused && event.keyCode === KeyCode.Tab) { + return; + } this.hideContentHover(); } From 36d8719a8e34fd831ace3960d63d35e320b9b92b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:37:16 +0100 Subject: [PATCH 309/479] SCM Input - respect `editor.emptySelectionClipboard` setting (#236361) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index fd25da9f13c..8a05bb6c06f 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1477,6 +1477,7 @@ class SCMInputWidgetEditorOptions { e => { return e.affectsConfiguration('editor.accessibilitySupport') || e.affectsConfiguration('editor.cursorBlinking') || + e.affectsConfiguration('editor.emptySelectionClipboard') || e.affectsConfiguration('editor.fontFamily') || e.affectsConfiguration('editor.rulers') || e.affectsConfiguration('editor.wordWrap') || @@ -1490,21 +1491,14 @@ class SCMInputWidgetEditorOptions { } getEditorConstructionOptions(): IEditorConstructionOptions { - const fontFamily = this._getEditorFontFamily(); - const fontSize = this._getEditorFontSize(); - const lineHeight = this._getEditorLineHeight(fontSize); - return { ...getSimpleEditorOptions(this.configurationService), - ...this._getEditorLanguageConfiguration(), + ...this.getEditorOptions(), cursorWidth: 1, dragAndDrop: true, dropIntoEditor: { enabled: true }, - fontFamily: fontFamily, - fontSize: fontSize, formatOnType: true, lineDecorationsWidth: 6, - lineHeight: lineHeight, overflowWidgetsDomNode: this.overflowWidgetsDomNode, padding: { top: 2, bottom: 2 }, quickSuggestions: false, @@ -1524,8 +1518,9 @@ class SCMInputWidgetEditorOptions { const lineHeight = this._getEditorLineHeight(fontSize); const accessibilitySupport = this.configurationService.getValue<'auto' | 'off' | 'on'>('editor.accessibilitySupport'); const cursorBlinking = this.configurationService.getValue<'blink' | 'smooth' | 'phase' | 'expand' | 'solid'>('editor.cursorBlinking'); + const emptySelectionClipboard = this.configurationService.getValue('editor.emptySelectionClipboard') === true; - return { ...this._getEditorLanguageConfiguration(), accessibilitySupport, cursorBlinking, fontFamily, fontSize, lineHeight }; + return { ...this._getEditorLanguageConfiguration(), accessibilitySupport, cursorBlinking, fontFamily, fontSize, lineHeight, emptySelectionClipboard }; } private _getEditorFontFamily(): string { From 422927459007119b4c5bde6dea39b34eefb314ae Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 17 Dec 2024 16:49:21 +0100 Subject: [PATCH 310/479] Fixes https://github.com/microsoft/vscode-copilot/issues/11377 (#236362) --- .../view/inlineEdits/sideBySideDiff.ts | 21 ++++++++++++++++--- .../browser/view/inlineEdits/utils.ts | 9 +++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 0df68982f14..d58c42f83f9 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -8,7 +8,7 @@ import { IAction } from '../../../../../../base/common/actions.js'; import { Color } from '../../../../../../base/common/color.js'; import { structuralEquals } from '../../../../../../base/common/equals.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IObservable, autorun, constObservable, derived, derivedOpts, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; +import { IObservable, autorun, constObservable, derived, derivedObservableWithCache, derivedOpts, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; import { MenuId, MenuItemAction } from '../../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; @@ -335,6 +335,22 @@ export class InlineEditsSideBySideDiff extends Disposable { private readonly _originalVerticalStartPosition = this._editorObs.observePosition(this._originalStartPosition, this._store).map(p => p?.y); private readonly _originalVerticalEndPosition = this._editorObs.observePosition(this._originalEndPosition, this._store).map(p => p?.y); + private readonly _originalDisplayRange = this._uiState.map(s => s?.originalDisplayRange); + private readonly _editorMaxContentWidthInRange = derived(this, reader => { + const originalDisplayRange = this._originalDisplayRange.read(reader); + if (!originalDisplayRange) { + return constObservable(0); + } + this._editorObs.versionId.read(reader); + + // Take the max value that we observed. + // Reset when either the edit changes or the editor text version. + return derivedObservableWithCache(this, (reader, lastValue) => { + const maxWidth = maxContentWidthInRange(this._editorObs, originalDisplayRange, reader); + return Math.max(maxWidth, lastValue ?? 0); + }); + }).map((v, r) => v.read(r)); + /** * ![test](./layout.dio.svg) */ @@ -352,7 +368,7 @@ export class InlineEditsSideBySideDiff extends Disposable { const horizontalScrollOffset = this._editorObs.scrollLeft.read(reader); - const editorContentMaxWidthInRange = maxContentWidthInRange(this._editorObs, state.originalDisplayRange, reader); + const editorContentMaxWidthInRange = this._editorMaxContentWidthInRange.read(reader); const editorLayout = this._editorObs.layoutInfo.read(reader); const previewContentWidth = this._previewEditorWidth.read(reader); const editorContentAreaWidth = editorLayout.contentWidth - editorLayout.verticalScrollbarWidth; @@ -389,7 +405,6 @@ export class InlineEditsSideBySideDiff extends Disposable { } else { desiredPreviewEditorScrollLeft = horizontalScrollOffset - previewEditorLeftInTextArea; left = editorLayout.contentLeft; - } const selectionTop = this._originalVerticalStartPosition.read(reader) ?? this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index af375cca816..d9928a05b2e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -16,6 +16,7 @@ import { URI } from '../../../../../../base/common/uri.js'; import { MenuEntryActionViewItem } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { Point } from '../../../../../browser/point.js'; +import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../common/core/offsetRange.js'; import { Position } from '../../../../../common/core/position.js'; @@ -34,7 +35,13 @@ export function maxContentWidthInRange(editor: ObservableCodeEditor, range: Line editor.scrollTop.read(reader); for (let i = range.startLineNumber; i < range.endLineNumberExclusive; i++) { const column = model.getLineMaxColumn(i); - const lineContentWidth = editor.editor.getOffsetForColumn(i, column); + let lineContentWidth = editor.editor.getOffsetForColumn(i, column); + if (lineContentWidth === -1) { + // approximation + const typicalHalfwidthCharacterWidth = editor.editor.getOption(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; + const approximation = column * typicalHalfwidthCharacterWidth; + lineContentWidth = approximation; + } maxContentWidth = Math.max(maxContentWidth, lineContentWidth); } const lines = range.mapToLineArray(l => model.getLineContent(l)); From 68410e1431f9849547b67d0b38dbc76cdb4aa3c8 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 17 Dec 2024 17:03:46 +0100 Subject: [PATCH 311/479] Fixes https://github.com/microsoft/vscode-copilot/issues/9375 (#236363) --- .../browser/model/inlineCompletionsModel.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index 65201933d14..b2669bdf25f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -227,6 +227,7 @@ export class InlineCompletionsModel extends Disposable { this._onlyRequestInlineEditsSignal.trigger(tx); } this._isActive.set(true, tx); + this._inAcceptFlow.set(true, tx); this._forceUpdateExplicitlySignal.trigger(tx); }); await this._fetchInlineCompletionsPromise.get(); @@ -480,6 +481,10 @@ export class InlineCompletionsModel extends Disposable { }); private readonly _tabShouldIndent = derived(this, reader => { + if (this._inAcceptFlow.read(reader)) { + return false; + } + function isMultiLine(range: Range): boolean { return range.startLineNumber !== range.endLineNumber; } From 639c4c8d1e63eb05f4061202e65311ec6ea4a940 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:57:23 +0100 Subject: [PATCH 312/479] Make title bar and command center visible when actions are enabled (#236369) Make title bar and or command center visible when actions are enabled in title bar --- src/vs/workbench/browser/layout.ts | 37 ++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index b60d2864283..84e2eaadb3c 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -116,12 +116,18 @@ interface IInitialEditorsState { readonly layout?: EditorGroupLayout; } +const COMMAND_CENTER_SETTINGS = [ + 'chat.commandCenter.enabled', + 'workbench.navigationControl.enabled', + 'workbench.experimental.share.enabled', +]; + export const TITLE_BAR_SETTINGS = [ LayoutSettings.ACTIVITY_BAR_LOCATION, LayoutSettings.COMMAND_CENTER, + ...COMMAND_CENTER_SETTINGS, LayoutSettings.EDITOR_ACTIONS_LOCATION, LayoutSettings.LAYOUT_ACTIONS, - 'workbench.navigationControl.enabled', 'window.menuBarVisibility', TitleBarSetting.TITLE_BAR_STYLE, TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, @@ -355,18 +361,31 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi LegacyWorkbenchLayoutSettings.SIDEBAR_POSITION, LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE, ].some(setting => e.affectsConfiguration(setting))) { - // Show Custom TitleBar if actions moved to the titlebar - const editorActionsMovedToTitlebar = e.affectsConfiguration(LayoutSettings.EDITOR_ACTIONS_LOCATION) && this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION) === EditorActionsLocation.TITLEBAR; - - let activityBarMovedToTopOrBottom = false; - if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) { - const activityBarPosition = this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION); - activityBarMovedToTopOrBottom = activityBarPosition === ActivityBarPosition.TOP || activityBarPosition === ActivityBarPosition.BOTTOM; + // Show Command Center if command center actions enabled + const shareEnabled = e.affectsConfiguration('workbench.experimental.share.enabled') && this.configurationService.getValue('workbench.experimental.share.enabled'); + const navigationControlEnabled = e.affectsConfiguration('workbench.navigationControl.enabled') && this.configurationService.getValue('workbench.navigationControl.enabled'); + + // Currently not supported for "chat.commandCenter.enabled" as we + // programatically set this during setup and could lead to unwanted titlebar appearing + // const chatControlsEnabled = e.affectsConfiguration('chat.commandCenter.enabled') && this.configurationService.getValue('chat.commandCenter.enabled'); + + if (shareEnabled || navigationControlEnabled) { + if (this.configurationService.getValue(LayoutSettings.COMMAND_CENTER) === false) { + this.configurationService.updateValue(LayoutSettings.COMMAND_CENTER, true); + return; // onDidChangeConfiguration will be triggered again + } } - if (activityBarMovedToTopOrBottom || editorActionsMovedToTitlebar) { + // Show Custom TitleBar if actions enabled in (or moved to) the titlebar + const editorActionsMovedToTitlebar = e.affectsConfiguration(LayoutSettings.EDITOR_ACTIONS_LOCATION) && this.configurationService.getValue(LayoutSettings.EDITOR_ACTIONS_LOCATION) === EditorActionsLocation.TITLEBAR; + const commandCenterEnabled = e.affectsConfiguration(LayoutSettings.COMMAND_CENTER) && this.configurationService.getValue(LayoutSettings.COMMAND_CENTER); + const layoutControlsEnabled = e.affectsConfiguration(LayoutSettings.LAYOUT_ACTIONS) && this.configurationService.getValue(LayoutSettings.LAYOUT_ACTIONS); + const activityBarMovedToTopOrBottom = e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION) && [ActivityBarPosition.TOP, ActivityBarPosition.BOTTOM].includes(this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION)); + + if (activityBarMovedToTopOrBottom || editorActionsMovedToTitlebar || commandCenterEnabled || layoutControlsEnabled) { if (this.configurationService.getValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY) === CustomTitleBarVisibility.NEVER) { this.configurationService.updateValue(TitleBarSetting.CUSTOM_TITLE_BAR_VISIBILITY, CustomTitleBarVisibility.AUTO); + return; // onDidChangeConfiguration will be triggered again } } From b4c9953da47ea1a2061397303db782bd67040fb7 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 17 Dec 2024 11:10:39 -0600 Subject: [PATCH 313/479] request folders/files when appropriate (#236370) fix #236368 --- .../src/terminalSuggestMain.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 5071c4c79ce..4acd5b59f8a 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -328,16 +328,6 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon } } } - const shouldShowCommands = !terminalContext.commandLine.substring(0, terminalContext.cursorPosition).trimStart().includes(' '); - if (shouldShowCommands && (filesRequested === foldersRequested)) { - // Include builitin/available commands in the results - const labels = new Set(items.map(i => i.label)); - for (const command of availableCommands) { - if (!labels.has(command)) { - items.push(createCompletionItem(terminalContext.cursorPosition, prefix, command)); - } - } - } const shouldShowResourceCompletions = ( @@ -351,6 +341,17 @@ export async function getCompletionItemsFromSpecs(specs: Fig.Spec[], terminalCon // and neither files nor folders are going to be requested (for a specific spec's argument) && (!filesRequested && !foldersRequested); + const shouldShowCommands = !terminalContext.commandLine.substring(0, terminalContext.cursorPosition).trimStart().includes(' '); + if (shouldShowCommands && (filesRequested === foldersRequested)) { + // Include builitin/available commands in the results + const labels = new Set(items.map(i => i.label)); + for (const command of availableCommands) { + if (!labels.has(command)) { + items.push(createCompletionItem(terminalContext.cursorPosition, prefix, command)); + } + } + } + if (shouldShowResourceCompletions) { filesRequested = true; foldersRequested = true; From d12587c914bebf0e7e878ed9d7eeb5d4fbd9d7cd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 Dec 2024 09:24:26 -0800 Subject: [PATCH 314/479] Fix windows test --- .../test/browser/services/decorationRenderOptions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index 9608d1d779a..ef5cd93a887 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -125,7 +125,7 @@ suite('Decoration Render Options', () => { if (platform.isWindows) { // windows file path (used as string) s.registerDecorationType('test', 'example', { gutterIconPath: URI.file('c:\\files\\miles\\more.png') }); - assertBackground('file:///c:/files/miles/more.png', 'vscode-file://vscode-app/c:/files/miles/more.png'); + assertBackground(CSS.escape('file:///c:/files/miles/more.png'), CSS.escape('vscode-file://vscode-app/c:/files/miles/more.png')); s.removeDecorationType('example'); // single quote must always be escaped/encoded From da1d8b9c8e1daaa9c9b7c4bd86590e7d374d3f29 Mon Sep 17 00:00:00 2001 From: Parasaran Date: Tue, 17 Dec 2024 22:57:12 +0530 Subject: [PATCH 315/479] fix 235221: Passing the markdown content to the webview via meta tag and purifying it before use --- extensions/markdown-language-features/preview-src/index.ts | 6 ++++++ .../src/preview/documentRenderer.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 33c84e0a384..d7fa1f18d76 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -353,6 +353,12 @@ document.addEventListener('click', event => { } }, true); +window.addEventListener('load', () => { + const htmlParser = new DOMParser(); + const markDownHtml = htmlParser.parseFromString(getData('data-md-content'), 'text/html'); + document.body.appendChild(markDownHtml.body); +}); + window.addEventListener('scroll', throttle(() => { updateScrollProgress(); diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index 331fb5566a0..f2447532e00 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -98,13 +98,13 @@ export class MdDocumentRenderer { + data-state="${escapeAttribute(JSON.stringify(state || {}))}" + data-md-content="${escapeAttribute(JSON.stringify(body.html))}"> ${this._getStyles(resourceProvider, sourceUri, config, imageInfo)} - ${body.html} ${this._getScripts(resourceProvider, nonce)} `; From 330ab6c2928963dd83a7d90a271d9f2eb8db90d2 Mon Sep 17 00:00:00 2001 From: Tony <68118705+Legend-Master@users.noreply.github.com> Date: Wed, 18 Dec 2024 01:50:35 +0800 Subject: [PATCH 316/479] Reland fix custom task shell doesn't work without manually passing in "run command" arg/flag (#236058) --- src/vs/platform/terminal/common/terminal.ts | 1 + .../tasks/browser/terminalTaskSystem.ts | 33 +++++++++++-------- .../browser/terminalProfileResolverService.ts | 1 + 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index c638fb625a2..8235e8a0ab4 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -871,6 +871,7 @@ export interface ITerminalProfile { overrideName?: boolean; color?: string; icon?: ThemeIcon | URI | { light: URI; dark: URI }; + isAutomationShell?: boolean; } export interface ITerminalDimensionsOverride extends Readonly { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index e44eaa47cf9..3b2c1e51c7f 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1112,7 +1112,6 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { color: task.configurationProperties.icon?.color || undefined, waitOnExit }; - let shellSpecified: boolean = false; const shellOptions: IShellConfiguration | undefined = task.command.options && task.command.options.shell; if (shellOptions) { if (shellOptions.executable) { @@ -1121,12 +1120,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig.args = undefined; } shellLaunchConfig.executable = await this._resolveVariable(variableResolver, shellOptions.executable); - shellSpecified = true; } if (shellOptions.args) { shellLaunchConfig.args = await this._resolveVariables(variableResolver, shellOptions.args.slice()); } } + const taskShellArgsSpecified = (defaultProfile.isAutomationShell ? shellLaunchConfig.args : shellOptions?.args) !== undefined; if (shellLaunchConfig.args === undefined) { shellLaunchConfig.args = []; } @@ -1139,29 +1138,34 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { windowsShellArgs = true; // If we don't have a cwd, then the terminal uses the home dir. const userHome = await this._pathService.userHome(); - if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { - return undefined; - } - if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { - if (!shellSpecified) { + if (basename === 'cmd.exe') { + if ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath))) { + return undefined; + } + if (!taskShellArgsSpecified) { + toAdd.push('/d', '/c'); + } + } else if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { + if (!taskShellArgsSpecified) { toAdd.push('-Command'); } } else if ((basename === 'bash.exe') || (basename === 'zsh.exe')) { windowsShellArgs = false; - if (!shellSpecified) { + if (!taskShellArgsSpecified) { toAdd.push('-c'); } } else if (basename === 'wsl.exe') { - if (!shellSpecified) { + if (!taskShellArgsSpecified) { toAdd.push('-e'); } } else { - if (!shellSpecified) { - toAdd.push('/d', '/c'); + if (!taskShellArgsSpecified) { + // Push `-c` for unknown shells if the user didn't specify the args + toAdd.push('-c'); } } } else { - if (!shellSpecified) { + if (!taskShellArgsSpecified) { // Under Mac remove -l to not start it as a login shell. if (platform === Platform.Platform.Mac) { // Background on -l on osx https://github.com/microsoft/vscode/issues/107563 @@ -1268,11 +1272,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const combinedShellArgs: string[] = Objects.deepClone(configuredShellArgs); shellCommandArgs.forEach(element => { const shouldAddShellCommandArg = configuredShellArgs.every((arg, index) => { - if ((arg.toLowerCase() === element) && (configuredShellArgs.length > index + 1)) { + const isDuplicated = arg.toLowerCase() === element.toLowerCase(); + if (isDuplicated && (configuredShellArgs.length > index + 1)) { // We can still add the argument, but only if not all of the following arguments begin with "-". return !configuredShellArgs.slice(index + 1).every(testArg => testArg.startsWith('-')); } else { - return arg.toLowerCase() !== element; + return !isDuplicated; } }); if (shouldAddShellCommandArg) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts index 6208cfc0413..e54a12a1b44 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts @@ -268,6 +268,7 @@ export abstract class BaseTerminalProfileResolverService extends Disposable impl const automationProfile = this._configurationService.getValue(`terminal.integrated.automationProfile.${this._getOsKey(options.os)}`); if (this._isValidAutomationProfile(automationProfile, options.os)) { automationProfile.icon = this._getCustomIcon(automationProfile.icon) || Codicon.tools; + automationProfile.isAutomationShell = true; return automationProfile; } From ca721227517f2ad0f737f0fde18df588beb96c67 Mon Sep 17 00:00:00 2001 From: Parasaran Date: Tue, 17 Dec 2024 23:32:42 +0530 Subject: [PATCH 317/479] fix 235221: Encode and decode markdown content to escape illegal chars --- extensions/markdown-language-features/preview-src/index.ts | 5 ++++- .../src/preview/documentRenderer.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index d7fa1f18d76..336245a1fdf 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -355,7 +355,10 @@ document.addEventListener('click', event => { window.addEventListener('load', () => { const htmlParser = new DOMParser(); - const markDownHtml = htmlParser.parseFromString(getData('data-md-content'), 'text/html'); + const markDownHtml = htmlParser.parseFromString( + decodeURIComponent(getData('data-md-content')), + 'text/html' + ); document.body.appendChild(markDownHtml.body); }); diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index f2447532e00..13e709c765f 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -99,7 +99,7 @@ export class MdDocumentRenderer { data-settings="${escapeAttribute(JSON.stringify(initialData))}" data-strings="${escapeAttribute(JSON.stringify(previewStrings))}" data-state="${escapeAttribute(JSON.stringify(state || {}))}" - data-md-content="${escapeAttribute(JSON.stringify(body.html))}"> + data-md-content="${escapeAttribute(JSON.stringify(encodeURIComponent(body.html)))}"> ${this._getStyles(resourceProvider, sourceUri, config, imageInfo)} From dfa26a2c5698f8bb8b0bb504d8cbc0207762dc4b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 17 Dec 2024 19:39:48 +0100 Subject: [PATCH 318/479] Adjusts zIndex for notebooks. (#236380) --- .../browser/view/inlineEdits/sideBySideDiff.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index d58c42f83f9..a64c52dcd90 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -616,7 +616,7 @@ export class InlineEditsSideBySideDiff extends Disposable { class: 'inline-edits-view', style: { overflow: 'visible', - zIndex: '10', + zIndex: '20', display: this._display, }, }, [ From 625bae23758002b62954fd10b53d25409421d718 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 17 Dec 2024 19:44:54 +0100 Subject: [PATCH 319/479] debt: clean up obsolete file usage (#236379) - remove it from scanner, with profiles reading this file is not needed - rename it usage for removal in management service --- .../common/extensionManagement.ts | 4 +- .../common/extensionsScannerService.ts | 51 ++---- .../node/extensionManagementService.ts | 163 +++++++++--------- .../node/extensionsWatcher.ts | 20 +-- .../node/extensionsScannerService.test.ts | 33 +--- 5 files changed, 115 insertions(+), 156 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 155079831fc..f08b46ae65b 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -456,8 +456,8 @@ export const enum ExtensionManagementErrorCode { Extract = 'Extract', Scanning = 'Scanning', ScanningExtension = 'ScanningExtension', - ReadUninstalled = 'ReadUninstalled', - UnsetUninstalled = 'UnsetUninstalled', + ReadRemoved = 'ReadRemoved', + UnsetRemoved = 'UnsetRemoved', Delete = 'Delete', Rename = 'Rename', IntializeDefaultProfile = 'IntializeDefaultProfile', diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 99868cddb5d..a186b6fa045 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -7,7 +7,6 @@ import { coalesce } from '../../../base/common/arrays.js'; import { ThrottledDelayer } from '../../../base/common/async.js'; import * as objects from '../../../base/common/objects.js'; import { VSBuffer } from '../../../base/common/buffer.js'; -import { IStringDictionary } from '../../../base/common/collections.js'; import { getErrorMessage } from '../../../base/common/errors.js'; import { getNodeType, parse, ParseError } from '../../../base/common/json.js'; import { getParseErrorMessage } from '../../../base/common/jsonErrorMessages.js'; @@ -18,12 +17,11 @@ import * as platform from '../../../base/common/platform.js'; import { basename, isEqual, joinPath } from '../../../base/common/resources.js'; import * as semver from '../../../base/common/semver/semver.js'; import Severity from '../../../base/common/severity.js'; -import { isEmptyObject } from '../../../base/common/types.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { IEnvironmentService } from '../../environment/common/environment.js'; import { IProductVersion, Metadata } from './extensionManagement.js'; -import { areSameExtensions, computeTargetPlatform, ExtensionKey, getExtensionId, getGalleryExtensionId } from './extensionManagementUtil.js'; +import { areSameExtensions, computeTargetPlatform, getExtensionId, getGalleryExtensionId } from './extensionManagementUtil.js'; import { ExtensionType, ExtensionIdentifier, IExtensionManifest, TargetPlatform, IExtensionIdentifier, IRelaxedExtensionManifest, UNDEFINED_PUBLISHER, IExtensionDescription, BUILTIN_MANIFEST_CACHE_FILE, USER_MANIFEST_CACHE_FILE, ExtensionIdentifierMap, parseEnabledApiProposalNames } from '../../extensions/common/extensions.js'; import { validateExtensionManifest } from '../../extensions/common/extensionValidator.js'; import { FileOperationResult, IFileService, toFileOperationResult } from '../../files/common/files.js'; @@ -106,7 +104,6 @@ export type ScanOptions = { readonly profileLocation?: URI; readonly includeInvalid?: boolean; readonly includeAllVersions?: boolean; - readonly includeUninstalled?: boolean; readonly checkControlFile?: boolean; readonly language?: string; readonly useCache?: boolean; @@ -145,10 +142,9 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private readonly _onDidChangeCache = this._register(new Emitter()); readonly onDidChangeCache = this._onDidChangeCache.event; - private readonly obsoleteFile = joinPath(this.userExtensionsLocation, '.obsolete'); - private readonly systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile)); - private readonly userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile)); - private readonly extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner, this.obsoleteFile)); + private readonly systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile)); + private readonly userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile)); + private readonly extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner)); constructor( readonly systemExtensionsLocation: URI, @@ -199,8 +195,8 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem const location = scanOptions.profileLocation ?? this.userExtensionsLocation; this.logService.trace('Started scanning user extensions', location); const profileScanOptions: IProfileExtensionsScanOptions | undefined = this.uriIdentityService.extUri.isEqual(scanOptions.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource) ? { bailOutWhenFileNotFound: true } : undefined; - const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion()); - const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner; + const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode ? this.userExtensionsCachedScanner : this.extensionsScanner; let extensions: IRelaxedScannedExtension[]; try { extensions = await extensionsScanner.scanExtensions(extensionsScannerInput); @@ -221,7 +217,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionDevelopmentLocationURI) { const extensions = (await Promise.all(this.environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file) .map(async extensionDevelopmentLocationURI => { - const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, true, scanOptions.language, false /* do not validate */, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, scanOptions.language, false /* do not validate */, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(input); return extensions.map(extension => { // Override the extension type from the existing extensions @@ -237,7 +233,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extension = await this.extensionsScanner.scanExtension(extensionsScannerInput); if (!extension) { return null; @@ -249,7 +245,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput); return this.applyScanOptions(extensions, extensionType, scanOptions, true); } @@ -405,7 +401,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private async scanDefaultSystemExtensions(useCache: boolean, language: string | undefined): Promise { this.logService.trace('Started scanning system extensions'); - const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, language, true, undefined, this.getProductVersion()); const extensionsScanner = useCache && !extensionsScannerInput.devMode ? this.systemExtensionsCachedScanner : this.extensionsScanner; const result = await extensionsScanner.scanExtensions(extensionsScannerInput); this.logService.trace('Scanned system extensions:', result.length); @@ -435,7 +431,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem break; } } - const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion()))))); + const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, language, true, undefined, this.getProductVersion()))))); this.logService.trace('Scanned dev system extensions:', result.length); return coalesce(result); } @@ -449,7 +445,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } } - private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, excludeObsolete: boolean, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise { + private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise { const translations = await this.getTranslations(language ?? platform.language); const mtime = await this.getMtime(location); const applicationExtensionsLocation = profile && !this.uriIdentityService.extUri.isEqual(location, this.userDataProfilesService.defaultProfile.extensionsResource) ? this.userDataProfilesService.defaultProfile.extensionsResource : undefined; @@ -462,7 +458,6 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem profile, profileScanOptions, type, - excludeObsolete, validate, productVersion.version, productVersion.date, @@ -504,7 +499,6 @@ export class ExtensionScannerInput { public readonly profile: boolean, public readonly profileScanOptions: IProfileExtensionsScanOptions | undefined, public readonly type: ExtensionType, - public readonly excludeObsolete: boolean, public readonly validate: boolean, public readonly productVersion: string, public readonly productDate: string | undefined, @@ -534,7 +528,6 @@ export class ExtensionScannerInput { && a.profile === b.profile && objects.equals(a.profileScanOptions, b.profileScanOptions) && a.type === b.type - && a.excludeObsolete === b.excludeObsolete && a.validate === b.validate && a.productVersion === b.productVersion && a.productDate === b.productDate @@ -558,7 +551,6 @@ class ExtensionsScanner extends Disposable { private readonly extensionsEnabledWithApiProposalVersion: string[]; constructor( - private readonly obsoleteFile: URI, @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService, @IFileService protected readonly fileService: IFileService, @@ -571,15 +563,9 @@ class ExtensionsScanner extends Disposable { } async scanExtensions(input: ExtensionScannerInput): Promise { - const extensions = input.profile ? await this.scanExtensionsFromProfile(input) : await this.scanExtensionsFromLocation(input); - let obsolete: IStringDictionary = {}; - if (input.excludeObsolete && input.type === ExtensionType.User) { - try { - const raw = (await this.fileService.readFile(this.obsoleteFile)).value.toString(); - obsolete = JSON.parse(raw); - } catch (error) { /* ignore */ } - } - return isEmptyObject(obsolete) ? extensions : extensions.filter(e => !obsolete[ExtensionKey.create(e).toString()]); + return input.profile + ? this.scanExtensionsFromProfile(input) + : this.scanExtensionsFromLocation(input); } private async scanExtensionsFromLocation(input: ExtensionScannerInput): Promise { @@ -596,7 +582,7 @@ class ExtensionsScanner extends Disposable { if (input.type === ExtensionType.User && basename(c.resource).indexOf('.') === 0) { return null; } - const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); return this.scanExtension(extensionScannerInput); })); return coalesce(extensions) @@ -622,7 +608,7 @@ class ExtensionsScanner extends Disposable { const extensions = await Promise.all( scannedProfileExtensions.map(async extensionInfo => { if (filter(extensionInfo)) { - const extensionScannerInput = new ExtensionScannerInput(extensionInfo.location, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + const extensionScannerInput = new ExtensionScannerInput(extensionInfo.location, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); return this.scanExtension(extensionScannerInput, extensionInfo.metadata); } return null; @@ -891,7 +877,6 @@ class CachedExtensionsScanner extends ExtensionsScanner { constructor( private readonly currentProfile: IUserDataProfile, - obsoleteFile: URI, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IUriIdentityService uriIdentityService: IUriIdentityService, @@ -900,7 +885,7 @@ class CachedExtensionsScanner extends ExtensionsScanner { @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService ) { - super(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService); + super(extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService); } override async scanExtensions(input: ExtensionScannerInput): Promise { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 92405eefb75..015c3dff393 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -60,7 +60,7 @@ export interface INativeServerExtensionManagementService extends IExtensionManag readonly _serviceBrand: undefined; scanAllUserInstalledExtensions(): Promise; scanInstalledExtensionAtLocation(location: URI): Promise; - markAsUninstalled(...extensions: IExtension[]): Promise; + deleteExtensions(...extensions: IExtension[]): Promise; } type ExtractExtensionResult = { readonly local: ILocalExtension; readonly verificationStatus?: ExtensionSignatureVerificationCode }; @@ -222,8 +222,8 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return this.extensionsScanner.copyExtensions(fromProfileLocation, toProfileLocation, { version: this.productService.version, date: this.productService.date }); } - markAsUninstalled(...extensions: IExtension[]): Promise { - return this.extensionsScanner.setUninstalled(...extensions); + deleteExtensions(...extensions: IExtension[]): Promise { + return this.extensionsScanner.setExtensionsForRemoval(...extensions); } async cleanUp(): Promise { @@ -480,8 +480,20 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi continue; } - // Check if this is a directory - if (!(await this.fileService.stat(resource)).isDirectory) { + // Ignore changes to the deleted folder + if (this.uriIdentityService.extUri.basename(resource).endsWith(DELETED_FOLDER_POSTFIX)) { + continue; + } + + try { + // Check if this is a directory + if (!(await this.fileService.stat(resource)).isDirectory) { + continue; + } + } catch (error) { + if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { + this.logService.error(error); + } continue; } @@ -502,23 +514,10 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi private async addExtensionsToProfile(extensions: [ILocalExtension, Metadata | undefined][], profileLocation: URI): Promise { const localExtensions = extensions.map(e => e[0]); - await this.setInstalled(localExtensions); + await this.extensionsScanner.unsetExtensionsForRemoval(...localExtensions.map(extension => ExtensionKey.create(extension))); await this.extensionsProfileScannerService.addExtensionsToProfile(extensions, profileLocation); this._onDidInstallExtensions.fire(localExtensions.map(local => ({ local, identifier: local.identifier, operation: InstallOperation.None, profileLocation }))); } - - private async setInstalled(extensions: ILocalExtension[]): Promise { - const uninstalled = await this.extensionsScanner.getUninstalledExtensions(); - for (const extension of extensions) { - const extensionKey = ExtensionKey.create(extension); - if (!uninstalled[extensionKey.toString()]) { - continue; - } - this.logService.trace('Removing the extension from uninstalled list:', extensionKey.id); - await this.extensionsScanner.setInstalled(extensionKey); - this.logService.info('Removed the extension from uninstalled list:', extensionKey.id); - } - } } type UpdateMetadataErrorClassification = { @@ -536,8 +535,8 @@ type UpdateMetadataErrorEvent = { export class ExtensionsScanner extends Disposable { - private readonly uninstalledResource: URI; - private readonly uninstalledFileLimiter: Queue; + private readonly obsoletedResource: URI; + private readonly obsoleteFileLimiter: Queue; private readonly _onExtract = this._register(new Emitter()); readonly onExtract = this._onExtract.event; @@ -555,13 +554,13 @@ export class ExtensionsScanner extends Disposable { @ILogService private readonly logService: ILogService, ) { super(); - this.uninstalledResource = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete'); - this.uninstalledFileLimiter = new Queue(); + this.obsoletedResource = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete'); + this.obsoleteFileLimiter = new Queue(); } async cleanUp(): Promise { await this.removeTemporarilyDeletedFolders(); - await this.removeUninstalledExtensions(); + await this.deleteExtensionsMarkedForRemoval(); await this.initializeMetadata(); } @@ -720,40 +719,38 @@ export class ExtensionsScanner extends Disposable { return this.scanLocalExtension(local.location, local.type, profileLocation); } - async getUninstalledExtensions(): Promise> { - try { - return await this.withUninstalledExtensions(); - } catch (error) { - throw toExtensionManagementError(error, ExtensionManagementErrorCode.ReadUninstalled); - } - } - - async setUninstalled(...extensions: IExtension[]): Promise { + async setExtensionsForRemoval(...extensions: IExtension[]): Promise { const extensionKeys: ExtensionKey[] = extensions.map(e => ExtensionKey.create(e)); - await this.withUninstalledExtensions(uninstalled => + await this.withRemovedExtensions(removedExtensions => extensionKeys.forEach(extensionKey => { - uninstalled[extensionKey.toString()] = true; - this.logService.info('Marked extension as uninstalled', extensionKey.toString()); + removedExtensions[extensionKey.toString()] = true; + this.logService.info('Marked extension as removed', extensionKey.toString()); })); } - async setInstalled(extensionKey: ExtensionKey): Promise { + async unsetExtensionsForRemoval(...extensionKeys: ExtensionKey[]): Promise { try { - await this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionKey.toString()]); + const results: boolean[] = []; + await this.withRemovedExtensions(removedExtensions => + extensionKeys.forEach(extensionKey => { + if (removedExtensions[extensionKey.toString()]) { + results.push(true); + delete removedExtensions[extensionKey.toString()]; + } else { + results.push(false); + } + })); + return results; } catch (error) { - throw toExtensionManagementError(error, ExtensionManagementErrorCode.UnsetUninstalled); + throw toExtensionManagementError(error, ExtensionManagementErrorCode.UnsetRemoved); } } - async removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { + async deleteExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { if (this.uriIdentityService.extUri.isEqualOrParent(extension.location, this.extensionsScannerService.userExtensionsLocation)) { return this.deleteExtensionFromLocation(extension.identifier.id, extension.location, type); } - } - - async removeUninstalledExtension(extension: ILocalExtension | IScannedExtension): Promise { - await this.removeExtension(extension, 'uninstalled'); - await this.withUninstalledExtensions(uninstalled => delete uninstalled[ExtensionKey.create(extension).toString()]); + await this.unsetExtensionsForRemoval(ExtensionKey.create(extension)); } async copyExtension(extension: ILocalExtension, fromProfileLocation: URI, toProfileLocation: URI, metadata: Partial): Promise { @@ -792,11 +789,11 @@ export class ExtensionsScanner extends Disposable { this.logService.info(`Deleted ${type} extension from disk`, id, location.fsPath); } - private withUninstalledExtensions(updateFn?: (uninstalled: IStringDictionary) => void): Promise> { - return this.uninstalledFileLimiter.queue(async () => { + private withRemovedExtensions(updateFn?: (removed: IStringDictionary) => void): Promise> { + return this.obsoleteFileLimiter.queue(async () => { let raw: string | undefined; try { - const content = await this.fileService.readFile(this.uninstalledResource, 'utf8'); + const content = await this.fileService.readFile(this.obsoletedResource, 'utf8'); raw = content.value.toString(); } catch (error) { if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { @@ -804,23 +801,23 @@ export class ExtensionsScanner extends Disposable { } } - let uninstalled = {}; + let removed = {}; if (raw) { try { - uninstalled = JSON.parse(raw); + removed = JSON.parse(raw); } catch (e) { /* ignore */ } } if (updateFn) { - updateFn(uninstalled); - if (Object.keys(uninstalled).length) { - await this.fileService.writeFile(this.uninstalledResource, VSBuffer.fromString(JSON.stringify(uninstalled))); + updateFn(removed); + if (Object.keys(removed).length) { + await this.fileService.writeFile(this.obsoletedResource, VSBuffer.fromString(JSON.stringify(removed))); } else { - await this.fileService.del(this.uninstalledResource); + await this.fileService.del(this.obsoletedResource); } } - return uninstalled; + return removed; }); } @@ -898,19 +895,25 @@ export class ExtensionsScanner extends Disposable { })); } - private async removeUninstalledExtensions(): Promise { - const uninstalled = await this.getUninstalledExtensions(); - if (Object.keys(uninstalled).length === 0) { - this.logService.debug(`No uninstalled extensions found.`); + private async deleteExtensionsMarkedForRemoval(): Promise { + let removed: IStringDictionary; + try { + removed = await this.withRemovedExtensions(); + } catch (error) { + throw toExtensionManagementError(error, ExtensionManagementErrorCode.ReadRemoved); + } + + if (Object.keys(removed).length === 0) { + this.logService.debug(`No extensions are marked as removed.`); return; } - this.logService.debug(`Removing uninstalled extensions:`, Object.keys(uninstalled)); + this.logService.debug(`Deleting extensions marked as removed:`, Object.keys(removed)); - const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeUninstalled: true, includeInvalid: true }); // All user extensions + const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeInvalid: true }); // All user extensions const installed: Set = new Set(); for (const e of extensions) { - if (!uninstalled[ExtensionKey.create(e).toString()]) { + if (!removed[ExtensionKey.create(e).toString()]) { installed.add(e.identifier.id.toLowerCase()); } } @@ -928,8 +931,8 @@ export class ExtensionsScanner extends Disposable { this.logService.error(error); } - const toRemove = extensions.filter(e => e.metadata /* Installed by System */ && uninstalled[ExtensionKey.create(e).toString()]); - await Promise.allSettled(toRemove.map(e => this.removeUninstalledExtension(e))); + const toRemove = extensions.filter(e => e.metadata /* Installed by System */ && removed[ExtensionKey.create(e).toString()]); + await Promise.allSettled(toRemove.map(e => this.deleteExtension(e, 'marked for removal'))); } private async removeTemporarilyDeletedFolders(): Promise { @@ -1021,7 +1024,7 @@ class InstallExtensionInProfileTask extends AbstractExtensionTask { - const uninstalled = await this.extensionsScanner.getUninstalledExtensions(); - if (!uninstalled[extensionKey.toString()]) { - return undefined; + private async unsetIfRemoved(extensionKey: ExtensionKey): Promise { + // If the same version of extension is marked as removed, remove it from there and return the local. + const [removed] = await this.extensionsScanner.unsetExtensionsForRemoval(extensionKey); + if (removed) { + this.logService.info('Removed the extension from removed list:', extensionKey.id); + const userExtensions = await this.extensionsScanner.scanAllUserExtensions(true); + return userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)); } - - this.logService.trace('Removing the extension from uninstalled list:', extensionKey.id); - // If the same version of extension is marked as uninstalled, remove it from there and return the local. - await this.extensionsScanner.setInstalled(extensionKey); - this.logService.info('Removed the extension from uninstalled list:', extensionKey.id); - - const userExtensions = await this.extensionsScanner.scanAllUserExtensions(true); - return userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)); + return undefined; } private async updateMetadata(extension: ILocalExtension, token: CancellationToken): Promise { @@ -1149,8 +1148,8 @@ class UninstallExtensionInProfileTask extends AbstractExtensionTask implem super(); } - protected async doRun(token: CancellationToken): Promise { - await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension, this.options.profileLocation); + protected doRun(token: CancellationToken): Promise { + return this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension, this.options.profileLocation); } } diff --git a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts index d2b65eaea55..2c4e976a5a6 100644 --- a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts +++ b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts @@ -48,7 +48,7 @@ export class ExtensionsWatcher extends Disposable { await this.extensionsScannerService.initializeDefaultProfileExtensions(); await this.onDidChangeProfiles(this.userDataProfilesService.profiles); this.registerListeners(); - await this.uninstallExtensionsNotInProfiles(); + await this.deleteExtensionsNotInProfiles(); } private registerListeners(): void { @@ -102,7 +102,7 @@ export class ExtensionsWatcher extends Disposable { } private async onDidRemoveExtensions(e: DidRemoveProfileExtensionsEvent): Promise { - const extensionsToUninstall: IExtension[] = []; + const extensionsToDelete: IExtension[] = []; const promises: Promise[] = []; for (const extension of e.extensions) { const key = this.getKey(extension.identifier, extension.version); @@ -115,7 +115,7 @@ export class ExtensionsWatcher extends Disposable { promises.push(this.extensionManagementService.scanInstalledExtensionAtLocation(extension.location) .then(result => { if (result) { - extensionsToUninstall.push(result); + extensionsToDelete.push(result); } else { this.logService.info('Extension not found at the location', extension.location.toString()); } @@ -125,8 +125,8 @@ export class ExtensionsWatcher extends Disposable { } try { await Promise.all(promises); - if (extensionsToUninstall.length) { - await this.uninstallExtensionsNotInProfiles(extensionsToUninstall); + if (extensionsToDelete.length) { + await this.deleteExtensionsNotInProfiles(extensionsToDelete); } } catch (error) { this.logService.error(error); @@ -180,13 +180,13 @@ export class ExtensionsWatcher extends Disposable { } } - private async uninstallExtensionsNotInProfiles(toUninstall?: IExtension[]): Promise { - if (!toUninstall) { + private async deleteExtensionsNotInProfiles(toDelete?: IExtension[]): Promise { + if (!toDelete) { const installed = await this.extensionManagementService.scanAllUserInstalledExtensions(); - toUninstall = installed.filter(installedExtension => !this.allExtensions.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); + toDelete = installed.filter(installedExtension => !this.allExtensions.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); } - if (toUninstall.length) { - await this.extensionManagementService.markAsUninstalled(...toUninstall); + if (toDelete.length) { + await this.extensionManagementService.deleteExtensions(...toDelete); } } diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts index 74d3ffcd738..551ba576d44 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts @@ -224,31 +224,6 @@ suite('NativeExtensionsScanerService Test', () => { assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); }); - test('scan exclude uninstalled extensions', async () => { - await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); - await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub' })); - await instantiationService.get(IFileService).writeFile(joinPath(URI.file(instantiationService.get(INativeEnvironmentService).extensionsPath), '.obsolete'), VSBuffer.fromString(JSON.stringify({ 'pub.name2-1.0.0': true }))); - const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); - - const actual = await testObject.scanUserExtensions({}); - - assert.deepStrictEqual(actual.length, 1); - assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); - }); - - test('scan include uninstalled extensions', async () => { - await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); - await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub' })); - await instantiationService.get(IFileService).writeFile(joinPath(URI.file(instantiationService.get(INativeEnvironmentService).extensionsPath), '.obsolete'), VSBuffer.fromString(JSON.stringify({ 'pub.name2-1.0.0': true }))); - const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); - - const actual = await testObject.scanUserExtensions({ includeUninstalled: true }); - - assert.deepStrictEqual(actual.length, 2); - assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); - assert.deepStrictEqual(actual[1].identifier, { id: 'pub.name2' }); - }); - test('scan include invalid extensions', async () => { await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub', engines: { vscode: '^1.67.0' } })); @@ -351,7 +326,7 @@ suite('ExtensionScannerInput', () => { ensureNoDisposablesAreLeakedInTestSuite(); test('compare inputs - location', () => { - const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(location, mtime, undefined, undefined, false, undefined, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(location, mtime, undefined, undefined, false, undefined, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, undefined), anInput(ROOT, undefined)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, 100), anInput(ROOT, 100)), true); @@ -361,7 +336,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - application location', () => { - const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(ROOT, undefined, location, mtime, false, undefined, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(ROOT, undefined, location, mtime, false, undefined, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, undefined), anInput(ROOT, undefined)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, 100), anInput(ROOT, 100)), true); @@ -371,7 +346,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - profile', () => { - const anInput = (profile: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, profile, profileScanOptions, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (profile: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, profile, profileScanOptions, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(true, { bailOutWhenFileNotFound: true }), anInput(true, { bailOutWhenFileNotFound: true })), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(false, { bailOutWhenFileNotFound: true }), anInput(false, { bailOutWhenFileNotFound: true })), true); @@ -384,7 +359,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - extension type', () => { - const anInput = (type: ExtensionType) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, false, undefined, type, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (type: ExtensionType) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, false, undefined, type, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ExtensionType.System), anInput(ExtensionType.System)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ExtensionType.User), anInput(ExtensionType.User)), true); From d3b582676fa88ec3c4bdfd350aafebf851fcda28 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 17 Dec 2024 12:57:14 -0600 Subject: [PATCH 320/479] add rerun action to terminal chat to align w editor (#236381) --- .../chat/browser/terminalChat.ts | 1 + .../chat/browser/terminalChatActions.ts | 45 +++++++++++++++++++ .../chat/browser/terminalChatWidget.ts | 2 + 3 files changed, 48 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts index b182074753e..5f16923d086 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChat.ts @@ -17,6 +17,7 @@ export const enum TerminalChatCommandId { InsertCommand = 'workbench.action.terminal.chat.insertCommand', InsertFirstCommand = 'workbench.action.terminal.chat.insertFirstCommand', ViewInChat = 'workbench.action.terminal.chat.viewInChat', + RerunRequest = 'workbench.action.terminal.chat.rerunRequest', } export const MENU_TERMINAL_CHAT_WIDGET_INPUT_SIDE_TOOLBAR = MenuId.for('terminalChatWidget'); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index 38bb507fe3b..deeb48f48c7 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -8,6 +8,9 @@ import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { localize2 } from '../../../../../nls.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { IChatWidgetService } from '../../../chat/browser/chat.js'; +import { ChatAgentLocation } from '../../../chat/common/chatAgents.js'; +import { IChatService } from '../../../chat/common/chatService.js'; import { AbstractInlineChatAction } from '../../../inlineChat/browser/inlineChatActions.js'; import { isDetachedTerminalInstance } from '../../../terminal/browser/terminal.js'; import { registerActiveXtermAction } from '../../../terminal/browser/terminalActions.js'; @@ -209,6 +212,48 @@ registerActiveXtermAction({ } }); +registerActiveXtermAction({ + id: TerminalChatCommandId.RerunRequest, + title: localize2('chat.rerun.label', "Rerun Request"), + f1: false, + icon: Codicon.refresh, + category: AbstractInlineChatAction.category, + precondition: ContextKeyExpr.and( + ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + TerminalChatContextKeys.requestActive.negate(), + ), + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KeyR + }, + menu: { + id: MENU_TERMINAL_CHAT_WIDGET_STATUS, + group: '0_main', + order: 5, + when: ContextKeyExpr.and(TerminalChatContextKeys.inputHasText.toNegated(), TerminalChatContextKeys.requestActive.negate()) + }, + run: async (_xterm, _accessor, activeInstance) => { + const chatService = _accessor.get(IChatService); + const chatWidgetService = _accessor.get(IChatWidgetService); + const contr = TerminalChatController.activeChatController; + const model = contr?.terminalChatWidget?.inlineChatWidget.chatWidget.viewModel?.model; + if (!model) { + return; + } + + const lastRequest = model.getRequests().at(-1); + if (lastRequest) { + const widget = chatWidgetService.getWidgetBySessionId(model.sessionId); + await chatService.resendRequest(lastRequest, { + noCommandDetection: false, + attempt: lastRequest.attempt + 1, + location: ChatAgentLocation.Terminal, + userSelectedModelId: widget?.input.currentLanguageModel + }); + } + } +}); + registerActiveXtermAction({ id: TerminalChatCommandId.ViewInChat, title: localize2('viewInChat', 'View in Chat'), diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index b668b55abc5..d1a310aae29 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -127,6 +127,8 @@ export class TerminalChatWidget extends Disposable { menu: MENU_TERMINAL_CHAT_WIDGET_STATUS, options: { buttonConfigProvider: action => ({ + showLabel: action.id !== TerminalChatCommandId.RerunRequest, + showIcon: action.id === TerminalChatCommandId.RerunRequest, isSecondary: action.id !== TerminalChatCommandId.RunCommand && action.id !== TerminalChatCommandId.RunFirstCommand }) } From d08f30ded43d3fef500c50a22d2476e896e6d447 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 17 Dec 2024 10:57:39 -0800 Subject: [PATCH 321/479] Update area labels (#236116) --- .vscode/notebooks/grooming.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 4a4955dd767..e408bbd7421 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -27,7 +27,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:chat -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering -label:cross-file-editing" + "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:chat -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-serialization -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering -label:cross-file-editing" }, { "kind": 1, From 2bb6383e85fdfc2c28bd479675b9b296ebc7f268 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 Dec 2024 10:59:05 -0800 Subject: [PATCH 322/479] Pick up latest TS nightly --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fd395ebd432..60dc545ca37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -153,7 +153,7 @@ "ts-node": "^10.9.1", "tsec": "0.2.7", "tslib": "^2.6.3", - "typescript": "^5.8.0-dev.20241212", + "typescript": "^5.8.0-dev.20241217", "typescript-eslint": "^8.8.0", "util": "^0.12.4", "webpack": "^5.94.0", @@ -17599,9 +17599,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.8.0-dev.20241212", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.0-dev.20241212.tgz", - "integrity": "sha512-DL+rd76Ze4iHIFTT6+f8SNdxkTYnR0cy6e0QRljOfyr2s0TrO2L9pAOB1dJnSizTAjxou7lIRpUWwxVOIyiMWg==", + "version": "5.8.0-dev.20241217", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.0-dev.20241217.tgz", + "integrity": "sha512-Q/I+eHfiwN0aWhitenThTT2FcA1lTlUZR1z+6d2WaD/8/wHfdjQjdHynCpYXjAwDkfG8Apf9LdzZ3rLRD3O9iQ==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index eb0abcc3221..8dd5e0a86a3 100644 --- a/package.json +++ b/package.json @@ -211,7 +211,7 @@ "ts-node": "^10.9.1", "tsec": "0.2.7", "tslib": "^2.6.3", - "typescript": "^5.8.0-dev.20241212", + "typescript": "^5.8.0-dev.20241217", "typescript-eslint": "^8.8.0", "util": "^0.12.4", "webpack": "^5.94.0", From 068676662c8067ea8f6a96ebcc9d9e9332e2b2ba Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 17 Dec 2024 20:05:05 +0100 Subject: [PATCH 323/479] Fixes https://github.com/microsoft/vscode-copilot/issues/10296 (#236383) --- .../controller/inlineCompletionsController.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 6be0ecd1c90..f3720436c2f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -38,6 +38,8 @@ import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js'; import { InlineCompletionsView } from '../view/inlineCompletionsView.js'; export class InlineCompletionsController extends Disposable { + private static readonly _instances = new Set(); + public static hot = createHotClass(InlineCompletionsController); public static ID = 'editor.contrib.inlineCompletionsController'; @@ -116,6 +118,22 @@ export class InlineCompletionsController extends Disposable { ) { super(); + InlineCompletionsController._instances.add(this); + this._register(toDisposable(() => InlineCompletionsController._instances.delete(this))); + + this._register(autorun(reader => { + // Cancel all other inline completions when a new one starts + const model = this.model.read(reader); + if (!model) { return; } + if (model.state.read(reader) !== undefined) { + for (const ctrl of InlineCompletionsController._instances) { + if (ctrl !== this) { + ctrl.reject(); + } + } + } + })); + this._register(runOnChange(this._editorObs.onDidType, (_value, _changes) => { if (this._enabled.get()) { this.model.get()?.trigger(); From e689b912ba8d46d224fc80ca56c641772476a7da Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 17 Dec 2024 12:12:49 -0700 Subject: [PATCH 324/479] Update telemetry package (#236378) --- extensions/git/package-lock.json | 141 ++++++++++-------- extensions/git/package.json | 2 +- .../github-authentication/package-lock.json | 141 ++++++++++-------- extensions/github-authentication/package.json | 2 +- extensions/github/package-lock.json | 141 ++++++++++-------- extensions/github/package.json | 2 +- .../html-language-features/package-lock.json | 141 ++++++++++-------- .../html-language-features/package.json | 2 +- .../json-language-features/package-lock.json | 141 ++++++++++-------- .../json-language-features/package.json | 2 +- .../package-lock.json | 141 ++++++++++-------- .../markdown-language-features/package.json | 2 +- extensions/media-preview/package-lock.json | 141 ++++++++++-------- extensions/media-preview/package.json | 2 +- extensions/merge-conflict/package-lock.json | 141 ++++++++++-------- extensions/merge-conflict/package.json | 2 +- .../package-lock.json | 141 ++++++++++-------- .../microsoft-authentication/package.json | 2 +- extensions/simple-browser/package-lock.json | 141 ++++++++++-------- extensions/simple-browser/package.json | 2 +- .../package-lock.json | 141 ++++++++++-------- .../typescript-language-features/package.json | 2 +- 22 files changed, 847 insertions(+), 726 deletions(-) diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index cf05db079db..bc150555c70 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", @@ -40,118 +40,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@tokenizer/token": { "version": "0.3.0", @@ -195,13 +205,14 @@ "dev": true }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/git/package.json b/extensions/git/package.json index 9aedfbb16e3..8b8f52643a8 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3471,7 +3471,7 @@ }, "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", diff --git a/extensions/github-authentication/package-lock.json b/extensions/github-authentication/package-lock.json index c150fa7acc3..cbc9e16b75f 100644 --- a/extensions/github-authentication/package-lock.json +++ b/extensions/github-authentication/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.2", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "node-fetch": "2.6.7", "vscode-tas-client": "^0.1.84" }, @@ -23,118 +23,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/mocha": { "version": "9.1.1", @@ -162,13 +172,14 @@ } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index cbc1ddc11dd..80b5d2c920e 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -61,7 +61,7 @@ }, "dependencies": { "node-fetch": "2.6.7", - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "vscode-tas-client": "^0.1.84" }, "devDependencies": { diff --git a/extensions/github/package-lock.json b/extensions/github/package-lock.json index 41de66d1a9b..1b7dc727a92 100644 --- a/extensions/github/package-lock.json +++ b/extensions/github/package-lock.json @@ -12,7 +12,7 @@ "@octokit/graphql": "5.0.5", "@octokit/graphql-schema": "14.4.0", "@octokit/rest": "19.0.4", - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "tunnel": "^0.0.6" }, "devDependencies": { @@ -23,118 +23,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@octokit/auth-token": { "version": "3.0.1", @@ -393,13 +403,14 @@ } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/github/package.json b/extensions/github/package.json index e84b35d19b7..f99a41d5979 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -183,7 +183,7 @@ "@octokit/graphql-schema": "14.4.0", "@octokit/rest": "19.0.4", "tunnel": "^0.0.6", - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/html-language-features/package-lock.json b/extensions/html-language-features/package-lock.json index fbb95e522dd..20b14561533 100644 --- a/extensions/html-language-features/package-lock.json +++ b/extensions/html-language-features/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "vscode-languageclient": "^10.0.0-next.13", "vscode-uri": "^3.0.8" }, @@ -21,118 +21,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -144,13 +154,14 @@ } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 60b9718e6c3..be411fe63a2 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -258,7 +258,7 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "vscode-languageclient": "^10.0.0-next.13", "vscode-uri": "^3.0.8" }, diff --git a/extensions/json-language-features/package-lock.json b/extensions/json-language-features/package-lock.json index f28a8c68df0..bde80cbfbe4 100644 --- a/extensions/json-language-features/package-lock.json +++ b/extensions/json-language-features/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "request-light": "^0.8.0", "vscode-languageclient": "^10.0.0-next.13" }, @@ -21,118 +21,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -144,13 +154,14 @@ } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index ff620435b98..cf4a7f162da 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -167,7 +167,7 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "request-light": "^0.8.0", "vscode-languageclient": "^10.0.0-next.13" }, diff --git a/extensions/markdown-language-features/package-lock.json b/extensions/markdown-language-features/package-lock.json index 91a1a6c5cb9..c13d2a007ad 100644 --- a/extensions/markdown-language-features/package-lock.json +++ b/extensions/markdown-language-features/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "dompurify": "^3.1.7", "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", @@ -39,118 +39,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/dompurify": { "version": "3.0.5", @@ -223,13 +233,14 @@ "dev": true }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 3f1c3a5fd58..6ed7ff1e62e 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -763,7 +763,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "dompurify": "^3.1.7", "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", diff --git a/extensions/media-preview/package-lock.json b/extensions/media-preview/package-lock.json index 68391b8c4be..d26855f3ad2 100644 --- a/extensions/media-preview/package-lock.json +++ b/extensions/media-preview/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "vscode-uri": "^3.0.6" }, "engines": { @@ -17,127 +17,138 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/media-preview/package.json b/extensions/media-preview/package.json index b42256a2260..e7ddad4354e 100644 --- a/extensions/media-preview/package.json +++ b/extensions/media-preview/package.json @@ -126,7 +126,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "vscode-uri": "^3.0.6" }, "repository": { diff --git a/extensions/merge-conflict/package-lock.json b/extensions/merge-conflict/package-lock.json index a57272606cd..5ee68d290f0 100644 --- a/extensions/merge-conflict/package-lock.json +++ b/extensions/merge-conflict/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/node": "20.x" @@ -19,118 +19,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -142,13 +152,14 @@ } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index cdda46fab32..de56c9c22cf 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -166,7 +166,7 @@ } }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/microsoft-authentication/package-lock.json b/extensions/microsoft-authentication/package-lock.json index 9f69f13972c..4aae40c5f17 100644 --- a/extensions/microsoft-authentication/package-lock.json +++ b/extensions/microsoft-authentication/package-lock.json @@ -12,7 +12,7 @@ "@azure/ms-rest-azure-env": "^2.0.0", "@azure/msal-node": "^2.16.2", "@azure/msal-node-extensions": "^1.5.0", - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, @@ -78,118 +78,128 @@ "license": "MIT" }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -235,13 +245,14 @@ "dev": true }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index c8ad1c070e2..ba7d83aa359 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -142,7 +142,7 @@ "@azure/ms-rest-azure-env": "^2.0.0", "@azure/msal-node": "^2.16.2", "@azure/msal-node-extensions": "^1.5.0", - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, diff --git a/extensions/simple-browser/package-lock.json b/extensions/simple-browser/package-lock.json index d3542946e63..c6d9b23636a 100644 --- a/extensions/simple-browser/package-lock.json +++ b/extensions/simple-browser/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/vscode-webview": "^1.57.0", @@ -20,118 +20,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/vscode-webview": { "version": "1.57.0", @@ -147,13 +157,14 @@ "license": "CC-BY-4.0" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index 0d812db6078..9aba9ad2503 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -66,7 +66,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/vscode-webview": "^1.57.0", diff --git a/extensions/typescript-language-features/package-lock.json b/extensions/typescript-language-features/package-lock.json index 6a19dce700f..4f97eb1048c 100644 --- a/extensions/typescript-language-features/package-lock.json +++ b/extensions/typescript-language-features/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "@vscode/sync-api-client": "^0.7.2", "@vscode/sync-api-common": "^0.7.2", "@vscode/sync-api-service": "^0.7.3", @@ -28,118 +28,128 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -157,13 +167,14 @@ "dev": true }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index c6363dfc48e..65038311f76 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -40,7 +40,7 @@ "Programming Languages" ], "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "@vscode/sync-api-client": "^0.7.2", "@vscode/sync-api-common": "^0.7.2", "@vscode/sync-api-service": "^0.7.3", From 1649b305c65c99dcc7b48ee1bf92453555f73203 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 17 Dec 2024 11:13:06 -0800 Subject: [PATCH 325/479] testing: fix can toggle inline test coverage for non-text files outside the workspace (#236386) Fixes #235346 --- .../contrib/testing/browser/codeCoverageDecorations.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts index 0d85cf22e3a..f82948d3106 100644 --- a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts @@ -39,9 +39,8 @@ import { KeybindingWeight } from '../../../../platform/keybinding/common/keybind import { ILogService } from '../../../../platform/log/common/log.js'; import { bindContextKey, observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; import { IQuickInputService, QuickPickInput } from '../../../../platform/quickinput/common/quickInput.js'; -import * as coverUtils from './codeCoverageDisplayUtils.js'; -import { testingCoverageMissingBranch, testingCoverageReport, testingFilterIcon, testingRerunIcon } from './icons.js'; -import { ManagedTestCoverageBars } from './testCoverageBars.js'; +import { ActiveEditorContext } from '../../../common/contextkeys.js'; +import { TEXT_FILE_EDITOR_ID } from '../../files/common/files.js'; import { getTestingConfiguration, TestingConfigKeys } from '../common/configuration.js'; import { TestCommandId } from '../common/constants.js'; import { FileCoverage } from '../common/testCoverage.js'; @@ -50,6 +49,9 @@ import { TestId } from '../common/testId.js'; import { ITestService } from '../common/testService.js'; import { CoverageDetails, DetailType, IDeclarationCoverage, IStatementCoverage } from '../common/testTypes.js'; import { TestingContextKeys } from '../common/testingContextKeys.js'; +import * as coverUtils from './codeCoverageDisplayUtils.js'; +import { testingCoverageMissingBranch, testingCoverageReport, testingFilterIcon, testingRerunIcon } from './icons.js'; +import { ManagedTestCoverageBars } from './testCoverageBars.js'; const CLASS_HIT = 'coverage-deco-hit'; const CLASS_MISS = 'coverage-deco-miss'; @@ -772,6 +774,7 @@ registerAction2(class FilterCoverageToTestInEditor extends Action2 { TestingContextKeys.isTestCoverageOpen, TestingContextKeys.coverageToolbarEnabled.notEqualsTo(true), TestingContextKeys.hasPerTestCoverage, + ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), ), group: 'navigation', }, From 9bb364f3328e5fda91c2e3b77a557e2927653030 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 17 Dec 2024 11:17:01 -0800 Subject: [PATCH 326/479] testing: fix scrollbar overlaps coverage indicators (#236387) Fixes #235343 --- src/vs/workbench/contrib/testing/browser/media/testing.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 2ebd1fc35bc..d56ac88a185 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -481,7 +481,7 @@ align-items: center; gap: 4px; font-size: 11px; - margin-right: 0.8em; + margin-right: 12px; } .test-coverage-bars .bar { From 6baeecd419a5197b1077d3a8e5e16e922c558258 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 17 Dec 2024 11:23:55 -0800 Subject: [PATCH 327/479] Add my labels (#236391) --- .vscode/notebooks/grooming.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index e408bbd7421..6d9deb71792 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -27,7 +27,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:chat -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-serialization -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering -label:cross-file-editing" + "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:chat -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-serialization -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering -label:cross-file-editing -label:microsoft-authentication -label:github-authentication -label:lm-access -label:secret-storage" }, { "kind": 1, From 7722c2bb0f4b17e1fa4cee6a24d3f0f438348ba9 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 Dec 2024 20:27:55 +0100 Subject: [PATCH 328/479] Git - adjust command `when` clauses (#236392) * Add telemetry for troubleshooting * Adjust command when clause --- extensions/git/package.json | 24 ++++++++++++------------ extensions/git/src/commands.ts | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 8b8f52643a8..08349fd4783 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -943,19 +943,19 @@ "command": "git.stageSelectedRanges", "key": "ctrl+k ctrl+alt+s", "mac": "cmd+k cmd+alt+s", - "when": "editorTextFocus && resourceScheme =~ /^git$|^file$/" + "when": "editorTextFocus && resourceScheme == file" }, { "command": "git.unstageSelectedRanges", "key": "ctrl+k ctrl+n", "mac": "cmd+k cmd+n", - "when": "isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "editorTextFocus && isInDiffEditor && isInDiffRightEditor && resourceScheme == git" }, { "command": "git.revertSelectedRanges", "key": "ctrl+k ctrl+r", "mac": "cmd+k cmd+r", - "when": "editorTextFocus && resourceScheme =~ /^git$|^file$/" + "when": "editorTextFocus && resourceScheme == file" } ], "menus": { @@ -1026,7 +1026,7 @@ }, { "command": "git.stageSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file" }, { "command": "git.stageChange", @@ -1034,7 +1034,7 @@ }, { "command": "git.revertSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file" }, { "command": "git.revertChange", @@ -1054,7 +1054,7 @@ }, { "command": "git.unstageSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && resourceScheme == git" }, { "command": "git.clean", @@ -2068,17 +2068,17 @@ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == git" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" }, { "command": "git.stashApplyEditor", @@ -2096,17 +2096,17 @@ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == git" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" } ], "editor/content": [ diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 64905cd55f3..8a313931201 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1563,12 +1563,16 @@ export class CommandCenter { return; } + this.logger.trace(`[CommandCenter][stageSelectedChanges] changes: ${JSON.stringify(changes)}`); + const modifiedDocument = textEditor.document; const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); const selectedChanges = changes .map(change => selectedLines.reduce((result, range) => result || intersectDiffWithRange(modifiedDocument, change, range), null)) .filter(d => !!d) as LineChange[]; + this.logger.trace(`[CommandCenter][stageSelectedChanges] selectedChanges: ${JSON.stringify(selectedChanges)}`); + if (!selectedChanges.length) { window.showInformationMessage(l10n.t('The selection range does not contain any changes.')); return; @@ -1745,6 +1749,8 @@ export class CommandCenter { return; } + this.logger.trace(`[CommandCenter][revertSelectedRanges] changes: ${JSON.stringify(changes)}`); + const modifiedDocument = textEditor.document; const selections = textEditor.selections; const selectedChanges = changes.filter(change => { @@ -1757,6 +1763,8 @@ export class CommandCenter { return; } + this.logger.trace(`[CommandCenter][revertSelectedRanges] selectedChanges: ${JSON.stringify(selectedChanges)}`); + const selectionsBeforeRevert = textEditor.selections; await this._revertChanges(textEditor, selectedChanges); textEditor.selections = selectionsBeforeRevert; @@ -1835,6 +1843,8 @@ export class CommandCenter { return; } + this.logger.trace(`[CommandCenter][unstageSelectedRanges] changes: ${JSON.stringify(changes)}`); + const originalUri = toGitUri(modifiedUri, 'HEAD'); const originalDocument = await workspace.openTextDocument(originalUri); const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); @@ -1848,8 +1858,11 @@ export class CommandCenter { } const invertedDiffs = selectedDiffs.map(invertLineChange); - const result = applyLineChanges(modifiedDocument, originalDocument, invertedDiffs); + this.logger.trace(`[CommandCenter][unstageSelectedRanges] selectedDiffs: ${JSON.stringify(selectedDiffs)}`); + this.logger.trace(`[CommandCenter][unstageSelectedRanges] invertedDiffs: ${JSON.stringify(invertedDiffs)}`); + + const result = applyLineChanges(modifiedDocument, originalDocument, invertedDiffs); await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); } From 7cc28c3e81227c60347c0ce507f6ee2283732c38 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 Dec 2024 11:30:32 -0800 Subject: [PATCH 329/479] Add markdown validation status item Fixes #236399 --- .../src/languageFeatures/diagnostics.ts | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index f54c136ad1a..8ea0cac41c1 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -72,10 +72,61 @@ class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider { } } +function registerMarkdownStatusItem(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable { + const statusItem = vscode.languages.createLanguageStatusItem('markdownStatus', selector); + + const enabledSettingId = 'validate.enabled'; + const commandId = '_markdown.toggleValidation'; + + const commandSub = commandManager.register({ + id: commandId, + execute: (enabled: boolean) => { + vscode.workspace.getConfiguration('markdown').update(enabledSettingId, enabled); + } + }); + + const update = () => { + const activeDoc = vscode.window.activeTextEditor?.document; + const markdownDoc = activeDoc?.languageId === 'markdown' ? activeDoc : undefined; + + const enabled = vscode.workspace.getConfiguration('markdown', markdownDoc).get(enabledSettingId); + if (enabled) { + statusItem.text = vscode.l10n.t('Link validation enabled'); + statusItem.command = { + command: commandId, + arguments: [false], + title: vscode.l10n.t('Disable'), + tooltip: vscode.l10n.t('Disable validation of Markdown links'), + }; + } else { + statusItem.text = vscode.l10n.t('Link validation disabled'); + statusItem.command = { + command: commandId, + arguments: [true], + title: vscode.l10n.t('Enable'), + tooltip: vscode.l10n.t('Enable validation of Markdown links'), + }; + } + }; + update(); + + return vscode.Disposable.from( + statusItem, + commandSub, + vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('markdown.' + enabledSettingId)) { + update(); + } + }), + ); +} export function registerDiagnosticSupport( selector: vscode.DocumentSelector, commandManager: CommandManager, ): vscode.Disposable { - return AddToIgnoreLinksQuickFixProvider.register(selector, commandManager); + return vscode.Disposable.from( + AddToIgnoreLinksQuickFixProvider.register(selector, commandManager), + registerMarkdownStatusItem(selector, commandManager), + ); } From a47b13ebc2d1b5db4d815ed9cde86278248cfaa7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 17 Dec 2024 11:59:05 -0800 Subject: [PATCH 330/479] Small cleanup follow up on #236145 - Don't send content as json - Reuse existing load helper --- .../preview-src/index.ts | 21 +++++++++---------- .../preview-src/settings.ts | 8 +++++-- .../src/preview/documentRenderer.ts | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 336245a1fdf..f9a0abc4f53 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -7,7 +7,7 @@ import { ActiveLineMarker } from './activeLineMarker'; import { onceDocumentLoaded } from './events'; import { createPosterForVsCode } from './messaging'; import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine, getLineElementForFragment } from './scroll-sync'; -import { SettingsManager, getData } from './settings'; +import { SettingsManager, getData, getRawData } from './settings'; import throttle = require('lodash.throttle'); import morphdom from 'morphdom'; import type { ToWebviewMessage } from '../types/previewMessaging'; @@ -61,8 +61,16 @@ function doAfterImagesLoaded(cb: () => void) { } onceDocumentLoaded(() => { - const scrollProgress = state.scrollProgress; + // Load initial html + const htmlParser = new DOMParser(); + const markDownHtml = htmlParser.parseFromString( + getRawData('data-initial-md-content'), + 'text/html' + ); + document.body.appendChild(markDownHtml.body); + // Restore + const scrollProgress = state.scrollProgress; addImageContexts(); if (typeof scrollProgress === 'number' && !settings.settings.fragment) { doAfterImagesLoaded(() => { @@ -353,15 +361,6 @@ document.addEventListener('click', event => { } }, true); -window.addEventListener('load', () => { - const htmlParser = new DOMParser(); - const markDownHtml = htmlParser.parseFromString( - decodeURIComponent(getData('data-md-content')), - 'text/html' - ); - document.body.appendChild(markDownHtml.body); -}); - window.addEventListener('scroll', throttle(() => { updateScrollProgress(); diff --git a/extensions/markdown-language-features/preview-src/settings.ts b/extensions/markdown-language-features/preview-src/settings.ts index 1bbe3477f25..0fb5d0c2686 100644 --- a/extensions/markdown-language-features/preview-src/settings.ts +++ b/extensions/markdown-language-features/preview-src/settings.ts @@ -16,18 +16,22 @@ export interface PreviewSettings { readonly webviewResourceRoot: string; } -export function getData(key: string): T { +export function getRawData(key: string): string { const element = document.getElementById('vscode-markdown-preview-data'); if (element) { const data = element.getAttribute(key); if (data) { - return JSON.parse(data); + return data; } } throw new Error(`Could not load data for ${key}`); } +export function getData(key: string): T { + return JSON.parse(getRawData(key)); +} + export class SettingsManager { private _settings: PreviewSettings = getData('data-settings'); diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index 13e709c765f..eeab8e19d9d 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -99,7 +99,7 @@ export class MdDocumentRenderer { data-settings="${escapeAttribute(JSON.stringify(initialData))}" data-strings="${escapeAttribute(JSON.stringify(previewStrings))}" data-state="${escapeAttribute(JSON.stringify(state || {}))}" - data-md-content="${escapeAttribute(JSON.stringify(encodeURIComponent(body.html)))}"> + data-initial-md-content="${escapeAttribute(body.html)}"> ${this._getStyles(resourceProvider, sourceUri, config, imageInfo)} From 20dc4d7d2647c3f69a55fd971f66c273666959a3 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 17 Dec 2024 21:37:33 +0100 Subject: [PATCH 331/479] Git - add more tracing to stage/unstage/revert commands (#236409) --- extensions/git/src/blame.ts | 13 ++++----- extensions/git/src/commands.ts | 26 +++++++++++++++++- extensions/git/src/staging.ts | 50 +++++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index 916d906a0a2..5c65fbcf1fc 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DecorationOptions, l10n, Position, Range, TextEditor, TextEditorChange, TextEditorDecorationType, TextEditorChangeKind, ThemeColor, Uri, window, workspace, EventEmitter, ConfigurationChangeEvent, StatusBarItem, StatusBarAlignment, Command, MarkdownString, TextEditorDiffInformation } from 'vscode'; +import { DecorationOptions, l10n, Position, Range, TextEditor, TextEditorChange, TextEditorDecorationType, TextEditorChangeKind, ThemeColor, Uri, window, workspace, EventEmitter, ConfigurationChangeEvent, StatusBarItem, StatusBarAlignment, Command, MarkdownString } from 'vscode'; import { Model } from './model'; import { dispose, fromNow, IDisposable } from './util'; import { Repository } from './repository'; @@ -11,6 +11,7 @@ import { throttle } from './decorators'; import { BlameInformation } from './git'; import { fromGitUri, isGitUri } from './uri'; import { emojify, ensureEmojis } from './emoji'; +import { getWorkingTreeAndIndexDiffInformation, getWorkingTreeDiffInformation } from './staging'; function lineRangesContainLine(changes: readonly TextEditorChange[], lineNumber: number): boolean { return changes.some(c => c.modified.startLineNumber <= lineNumber && lineNumber < c.modified.endLineNumberExclusive); @@ -277,10 +278,6 @@ export class GitBlameController { return blameInformation; } - private _findDiffInformation(textEditor: TextEditor, ref: string): TextEditorDiffInformation | undefined { - return textEditor.diffInformation?.find(diff => diff.original && isGitUri(diff.original) && fromGitUri(diff.original).ref === ref); - } - @throttle private async _updateTextEditorBlameInformation(textEditor: TextEditor | undefined, showBlameInformationForPositionZero = false): Promise { if (!textEditor?.diffInformation || textEditor !== window.activeTextEditor) { @@ -319,7 +316,7 @@ export class GitBlameController { workingTreeAndIndexChanges = undefined; } else if (ref === '') { // Resource on the right-hand side of the diff editor when viewing a resource from the index. - const diffInformationWorkingTreeAndIndex = this._findDiffInformation(textEditor, 'HEAD'); + const diffInformationWorkingTreeAndIndex = getWorkingTreeAndIndexDiffInformation(textEditor); // Working tree + index diff information is present and it is stale if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { @@ -333,7 +330,7 @@ export class GitBlameController { } } else { // Working tree diff information. Diff Editor (Working Tree) -> Text Editor - const diffInformationWorkingTree = this._findDiffInformation(textEditor, '~') ?? this._findDiffInformation(textEditor, ''); + const diffInformationWorkingTree = getWorkingTreeDiffInformation(textEditor); // Working tree diff information is not present or it is stale if (!diffInformationWorkingTree || diffInformationWorkingTree.isStale) { @@ -341,7 +338,7 @@ export class GitBlameController { } // Working tree + index diff information - const diffInformationWorkingTreeAndIndex = this._findDiffInformation(textEditor, 'HEAD'); + const diffInformationWorkingTreeAndIndex = getWorkingTreeAndIndexDiffInformation(textEditor); // Working tree + index diff information is present and it is stale if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 8a313931201..dde1c99049a 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -12,7 +12,7 @@ import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, Remo import { Git, Stash } from './git'; import { Model } from './model'; import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository'; -import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; +import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getModifiedRange, getWorkingTreeAndIndexDiffInformation, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges } from './staging'; import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri'; import { dispose, grep, isDefined, isDescendant, pathEquals, relativePath, truncate } from './util'; import { GitTimelineItem } from './timelineProvider'; @@ -1565,6 +1565,12 @@ export class CommandCenter { this.logger.trace(`[CommandCenter][stageSelectedChanges] changes: ${JSON.stringify(changes)}`); + const workingTreeDiffInformation = getWorkingTreeDiffInformation(textEditor); + if (workingTreeDiffInformation) { + this.logger.trace(`[CommandCenter][stageSelectedChanges] diffInformation: ${JSON.stringify(workingTreeDiffInformation)}`); + this.logger.trace(`[CommandCenter][stageSelectedChanges] diffInformation changes: ${JSON.stringify(toLineChanges(workingTreeDiffInformation))}`); + } + const modifiedDocument = textEditor.document; const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); const selectedChanges = changes @@ -1751,6 +1757,12 @@ export class CommandCenter { this.logger.trace(`[CommandCenter][revertSelectedRanges] changes: ${JSON.stringify(changes)}`); + const workingTreeDiffInformation = getWorkingTreeDiffInformation(textEditor); + if (workingTreeDiffInformation) { + this.logger.trace(`[CommandCenter][revertSelectedRanges] diffInformation: ${JSON.stringify(workingTreeDiffInformation)}`); + this.logger.trace(`[CommandCenter][revertSelectedRanges] diffInformation changes: ${JSON.stringify(toLineChanges(workingTreeDiffInformation))}`); + } + const modifiedDocument = textEditor.document; const selections = textEditor.selections; const selectedChanges = changes.filter(change => { @@ -1845,6 +1857,18 @@ export class CommandCenter { this.logger.trace(`[CommandCenter][unstageSelectedRanges] changes: ${JSON.stringify(changes)}`); + const workingTreeDiffInformation = getWorkingTreeDiffInformation(textEditor); + if (workingTreeDiffInformation) { + this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation (working tree): ${JSON.stringify(workingTreeDiffInformation)}`); + this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation changes (working tree): ${JSON.stringify(toLineChanges(workingTreeDiffInformation))}`); + } + + const workingTreeAndIndexDiffInformation = getWorkingTreeAndIndexDiffInformation(textEditor); + if (workingTreeAndIndexDiffInformation) { + this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation (working tree + index): ${JSON.stringify(workingTreeAndIndexDiffInformation)}`); + this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation changes (working tree + index): ${JSON.stringify(toLineChanges(workingTreeAndIndexDiffInformation))}`); + } + const originalUri = toGitUri(modifiedUri, 'HEAD'); const originalDocument = await workspace.openTextDocument(originalUri); const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 2dcc6d54487..38a462aedf6 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextDocument, Range, LineChange, Selection, Uri } from 'vscode'; +import { TextDocument, Range, LineChange, Selection, Uri, TextEditor, TextEditorDiffInformation } from 'vscode'; +import { fromGitUri, isGitUri } from './uri'; export function applyLineChanges(original: TextDocument, modified: TextDocument, diffs: LineChange[]): string { const result: string[] = []; @@ -143,6 +144,53 @@ export function invertLineChange(diff: LineChange): LineChange { }; } +export function toLineChanges(diffInformation: TextEditorDiffInformation): LineChange[] { + return diffInformation.changes.map(x => { + let originalStartLineNumber: number; + let originalEndLineNumber: number; + let modifiedStartLineNumber: number; + let modifiedEndLineNumber: number; + + if (x.original.startLineNumber === x.original.endLineNumberExclusive) { + // Insertion + originalStartLineNumber = x.original.startLineNumber - 1; + originalEndLineNumber = 0; + } else { + originalStartLineNumber = x.original.startLineNumber; + originalEndLineNumber = x.original.endLineNumberExclusive - 1; + } + + if (x.modified.startLineNumber === x.modified.endLineNumberExclusive) { + // Deletion + modifiedStartLineNumber = x.modified.startLineNumber - 1; + modifiedEndLineNumber = 0; + } else { + modifiedStartLineNumber = x.modified.startLineNumber; + modifiedEndLineNumber = x.modified.endLineNumberExclusive - 1; + } + + return { + originalStartLineNumber, + originalEndLineNumber, + modifiedStartLineNumber, + modifiedEndLineNumber + }; + }); +} + +export function getWorkingTreeDiffInformation(textEditor: TextEditor): TextEditorDiffInformation | undefined { + // Working tree diff information. Diff Editor (Working Tree) -> Text Editor + return getDiffInformation(textEditor, '~') ?? getDiffInformation(textEditor, ''); +} + +export function getWorkingTreeAndIndexDiffInformation(textEditor: TextEditor): TextEditorDiffInformation | undefined { + return getDiffInformation(textEditor, 'HEAD'); +} + +function getDiffInformation(textEditor: TextEditor, ref: string): TextEditorDiffInformation | undefined { + return textEditor.diffInformation?.find(diff => diff.original && isGitUri(diff.original) && fromGitUri(diff.original).ref === ref); +} + export interface DiffEditorSelectionHunkToolbarContext { mapping: unknown; /** From 999f28e49fee2e38050fd1a9c6ab37aa76841d6a Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 17 Dec 2024 12:39:39 -0800 Subject: [PATCH 332/479] Extend hover styling for entire sticky line (#236410) extend hover styling for entire sticky line --- .../browser/media/notebookEditorStickyScroll.css | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css index 25fa90546d6..a723d49cfe0 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css @@ -38,10 +38,8 @@ .monaco-workbench .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-element - .notebook-sticky-scroll-header:hover { + .notebook-sticky-scroll-element:hover { background-color: var(--vscode-editorStickyScrollHover-background); - width: 100%; cursor: pointer; } @@ -55,13 +53,11 @@ .monaco-workbench.hc-light .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-element - .notebook-sticky-scroll-header:hover, + .notebook-sticky-scroll-element:hover, .monaco-workbench.hc-black .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-element - .notebook-sticky-scroll-header:hover { + .notebook-sticky-scroll-element:hover { outline: 1px dashed var(--vscode-contrastActiveBorder); outline-offset: -2px; } From b0e6e13be600c35f7444f16b4c5f59eb503ec2f1 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 17 Dec 2024 13:34:38 -0800 Subject: [PATCH 333/479] notebook find replace position vs global toolbar (#236407) * notebook find replace position vs global toolbar * use runAndSubscribe --- .../contrib/find/notebookFindReplaceWidget.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index 6e8651a4a32..8509900d4b0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -20,6 +20,7 @@ import { Widget } from '../../../../../../base/browser/ui/widget.js'; import { Action, ActionRunner, IAction, IActionRunner, Separator } from '../../../../../../base/common/actions.js'; import { Delayer } from '../../../../../../base/common/async.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; +import { Event } from '../../../../../../base/common/event.js'; import { KeyCode } from '../../../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { isSafari } from '../../../../../../base/common/platform.js'; @@ -345,6 +346,17 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._domNode = document.createElement('div'); this._domNode.classList.add('simple-fr-find-part-wrapper'); + + this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { + if (!e || e.affectsConfiguration(NotebookSetting.globalToolbar)) { + if (this._notebookEditor.notebookOptions.getLayoutConfiguration().globalToolbar) { + this._domNode.style.top = '26px'; + } else { + this._domNode.style.top = '0px'; + } + } + })); + this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e))); this._scopedContextKeyService = contextKeyService.createScoped(this._domNode); From cece885ae0d18ae017007ccdd7fb808fa23d6b1e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 17 Dec 2024 14:59:26 -0800 Subject: [PATCH 334/479] testing: show item when coverage is filtered, standardize some tree classes (#236418) Fixes #235147 --- src/vs/platform/actions/common/actions.ts | 1 + .../contrib/testing/browser/media/testing.css | 78 ++++++------ .../testing/browser/testCoverageView.ts | 113 +++++++++++++++--- .../testResultsView/testResultsTree.ts | 8 +- .../testResultsView/testResultsViewContent.ts | 2 +- .../testing/browser/testingExplorerView.ts | 4 +- 6 files changed, 137 insertions(+), 69 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 7f00003c0f3..2b784b5bf5e 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -139,6 +139,7 @@ export class MenuId { static readonly TestPeekElement = new MenuId('TestPeekElement'); static readonly TestPeekTitle = new MenuId('TestPeekTitle'); static readonly TestCallStack = new MenuId('TestCallStack'); + static readonly TestCoverageFilterItem = new MenuId('TestCoverageFilterItem'); static readonly TouchBarContext = new MenuId('TouchBarContext'); static readonly TitleBarContext = new MenuId('TitleBarContext'); static readonly TitleBarTitleContext = new MenuId('TitleBarTitleContext'); diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index d56ac88a185..ef2437bd588 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -43,34 +43,41 @@ position: relative; } -.test-explorer .test-item .label, -.test-output-peek-tree .test-peek-item .name, -.test-coverage-list-item .name, -.test-coverage-list-item-label { - flex-grow: 1; - width: 0; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; +.testing-stdtree-container { + display: flex; + align-items: center; + + .label { + flex-grow: 1; + width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; - .monaco-list.horizontal-scrolling & { - width: auto; - overflow: visible; + .codicon { + vertical-align: middle; + font-size: 1em; + transform: scale(1.25); + margin: 0 0.125em; + } + + .monaco-list.horizontal-scrolling & { + width: auto; + overflow: visible; + } } -} -.test-output-peek-tree .test-peek-item .name .codicon, -.test-explorer .test-item .label .codicon { - vertical-align: middle; - font-size: 1em; - transform: scale(1.25); - margin: 0 0.125em; -} + .monaco-action-bar { + display: none; + flex-shrink: 0; + margin-right: 0.8em; + } -.test-explorer .test-item, -.test-output-peek-tree .test-peek-item { - display: flex; - align-items: center; + &:hover, &.focused { + .monaco-action-bar { + display: initial; + } + } } .test-output-peek-tree { @@ -78,14 +85,16 @@ border-left: 1px solid var(--vscode-panelSection-border); } -.test-output-peek-tree .monaco-list-row .monaco-action-bar, -.test-explorer .monaco-list-row .monaco-action-bar, .test-explorer .monaco-list-row .codicon-testing-hidden { display: none; flex-shrink: 0; margin-right: 0.8em; } +.test-explorer .monaco-list-row .monaco-action-bar.testing-is-continuous-run { + display: initial; +} + .test-explorer .monaco-list-row .monaco-action-bar .codicon-testing-continuous-is-on { color: var(--vscode-inputOption-activeForeground); border-color: var(--vscode-inputOption-activeBorder); @@ -102,14 +111,6 @@ display: block !important; } -.test-explorer .monaco-list-row:hover .monaco-action-bar, -.test-explorer .monaco-list-row.focused .monaco-action-bar, -.test-explorer .monaco-list-row .monaco-action-bar.testing-is-continuous-run, -.test-output-peek-tree .monaco-list-row:hover .monaco-action-bar, -.test-output-peek-tree .monaco-list-row.focused .monaco-action-bar { - display: initial; -} - .test-explorer .monaco-list-row .test-is-hidden .codicon-testing-hidden { display: block; margin-right: 9px; @@ -467,15 +468,6 @@ /** -- coverage */ -.coverage-view-is-filtered > .pane-header > .actions { - display: block !important; -} - -.test-coverage-list-item { - display: flex; - align-items: center; -} - .test-coverage-bars { display: flex; align-items: center; diff --git a/src/vs/workbench/contrib/testing/browser/testCoverageView.ts b/src/vs/workbench/contrib/testing/browser/testCoverageView.ts index 5ee19fb10f6..b660cc798b1 100644 --- a/src/vs/workbench/contrib/testing/browser/testCoverageView.ts +++ b/src/vs/workbench/contrib/testing/browser/testCoverageView.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from '../../../../base/browser/dom.js'; +import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; import { ICompressedTreeElement, ICompressedTreeNode } from '../../../../base/browser/ui/tree/compressedObjectTreeModel.js'; import { ICompressibleTreeRenderer } from '../../../../base/browser/ui/tree/objectTree.js'; @@ -24,7 +25,9 @@ import { Position } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { Action2, IMenuService, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; @@ -42,17 +45,17 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js import { IResourceLabel, ResourceLabels } from '../../../browser/labels.js'; import { IViewPaneOptions, ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js'; import { IViewDescriptorService } from '../../../common/views.js'; -import * as coverUtils from './codeCoverageDisplayUtils.js'; -import { testingStatesToIcons, testingWasCovered } from './icons.js'; -import { CoverageBarSource, ManagedTestCoverageBars } from './testCoverageBars.js'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; import { TestCommandId, Testing } from '../common/constants.js'; import { onObservableChange } from '../common/observableUtils.js'; import { BypassedFileCoverage, ComputedFileCoverage, FileCoverage, TestCoverage, getTotalCoveragePercent } from '../common/testCoverage.js'; import { ITestCoverageService } from '../common/testCoverageService.js'; import { TestId } from '../common/testId.js'; import { TestingContextKeys } from '../common/testingContextKeys.js'; -import { CoverageDetails, DetailType, ICoverageCount, IDeclarationCoverage, TestResultState } from '../common/testTypes.js'; -import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; +import { CoverageDetails, DetailType, ICoverageCount, IDeclarationCoverage, ITestItem, TestResultState } from '../common/testTypes.js'; +import * as coverUtils from './codeCoverageDisplayUtils.js'; +import { testingStatesToIcons, testingWasCovered } from './icons.js'; +import { CoverageBarSource, ManagedTestCoverageBars } from './testCoverageBars.js'; const enum CoverageSortOrder { Coverage, @@ -95,13 +98,6 @@ export class TestCoverageView extends ViewPane { this.tree.clear(); } })); - - this._register(autorun(reader => { - this.element.classList.toggle( - 'coverage-view-is-filtered', - !!this.coverageService.filterToTest.read(reader), - ); - })); } protected override layoutBody(height: number, width: number): void { @@ -197,6 +193,16 @@ class RevealUncoveredDeclarations { constructor(public readonly n: number) { } } +class CurrentlyFilteredTo { + public readonly id = String(fnNodeId++); + + public get label() { + return localize('filteredToTest', "Showing coverage for \"{0}\"", this.testItem.label); + } + + constructor(public readonly testItem: ITestItem) { } +} + class LoadingDetails { public readonly id = String(fnNodeId++); public readonly label = localize('loadingCoverageDetails', "Loading Coverage Details..."); @@ -204,7 +210,7 @@ class LoadingDetails { /** Type of nodes returned from {@link TestCoverage}. Note: value is *always* defined. */ type TestCoverageFileNode = IPrefixTreeNode; -type CoverageTreeElement = TestCoverageFileNode | DeclarationCoverageNode | LoadingDetails | RevealUncoveredDeclarations; +type CoverageTreeElement = TestCoverageFileNode | DeclarationCoverageNode | LoadingDetails | RevealUncoveredDeclarations | CurrentlyFilteredTo; const isFileCoverage = (c: CoverageTreeElement): c is TestCoverageFileNode => typeof c === 'object' && 'value' in c; const isDeclarationCoverage = (c: CoverageTreeElement): c is DeclarationCoverageNode => c instanceof DeclarationCoverageNode; @@ -221,9 +227,12 @@ class TestCoverageTree extends Disposable { sortOrder: IObservable, @IInstantiationService instantiationService: IInstantiationService, @IEditorService editorService: IEditorService, + @ICommandService commandService: ICommandService, ) { super(); + container.classList.add('testing-stdtree'); + this.tree = instantiationService.createInstance( WorkbenchCompressibleObjectTree, 'TestCoverageView', @@ -233,6 +242,7 @@ class TestCoverageTree extends Disposable { instantiationService.createInstance(FileCoverageRenderer, labels), instantiationService.createInstance(DeclarationCoverageRenderer), instantiationService.createInstance(BasicRenderer), + instantiationService.createInstance(CurrentlyFilteredToRenderer), ], { expandOnlyOnTwistieClick: true, @@ -289,6 +299,9 @@ class TestCoverageTree extends Disposable { } else if (isDeclarationCoverage(e.element)) { resource = e.element.uri; selection = e.element.location; + } else if (e.element instanceof CurrentlyFilteredTo) { + commandService.executeCommand(TestCommandId.CoverageFilterToTest); + return; } } if (!resource) { @@ -351,7 +364,19 @@ class TestCoverageTree extends Disposable { } })); - this.tree.setChildren(null, Iterable.map(files, toChild)); + let children = Iterable.map(files, toChild); + const filteredTo = showOnlyTest && coverage.result.getTestById(showOnlyTest.toString()); + if (filteredTo) { + children = Iterable.concat( + Iterable.single>({ + element: new CurrentlyFilteredTo(filteredTo), + incompressible: true, + }), + children, + ); + } + + this.tree.setChildren(null, children); } public layout(height: number, width: number) { @@ -409,6 +434,9 @@ class TestCoverageTreeListDelegate implements IListVirtualDelegate { } } +interface IFilteredToTemplate { + label: HTMLElement; + actions: ActionBar; +} + +class CurrentlyFilteredToRenderer implements ICompressibleTreeRenderer { + public static readonly ID = 'C'; + public readonly templateId = CurrentlyFilteredToRenderer.ID; + + constructor( + @IMenuService private readonly menuService: IMenuService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { } + + renderCompressedElements(node: ITreeNode, FuzzyScore>, index: number, templateData: IFilteredToTemplate, height: number | undefined): void { + this.renderInner(node.element.elements[node.element.elements.length - 1] as CurrentlyFilteredTo, templateData); + } + + renderTemplate(container: HTMLElement): IFilteredToTemplate { + container.classList.add('testing-stdtree-container'); + const label = dom.append(container, dom.$('.label')); + const menu = this.menuService.getMenuActions(MenuId.TestCoverageFilterItem, this.contextKeyService, { + shouldForwardArgs: true, + }); + + const actions = new ActionBar(container); + actions.push(getActionBarActions(menu, 'inline').primary, { icon: true, label: false }); + actions.domNode.style.display = 'block'; + + return { label, actions }; + } + + renderElement(element: ITreeNode, index: number, templateData: IFilteredToTemplate, height: number | undefined): void { + this.renderInner(element.element as CurrentlyFilteredTo, templateData); + } + + disposeTemplate(templateData: IFilteredToTemplate): void { + templateData.actions.dispose(); + } + + private renderInner(element: CurrentlyFilteredTo, container: IFilteredToTemplate) { + container.label.innerText = element.label; + } +} + interface FileTemplateData { container: HTMLElement; bars: ManagedTestCoverageBars; @@ -469,7 +542,7 @@ class FileCoverageRenderer implements ICompressibleTreeRenderer action instanceof MenuItemAction ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts index f93d2065f0c..421afad78f2 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts @@ -248,7 +248,7 @@ export class TestResultsViewContent extends Disposable { this.contextKeyTestMessage = TestingContextKeys.testMessageContext.bindTo(this.messageContextKeyService); this.contextKeyResultOutdated = TestingContextKeys.testResultOutdated.bindTo(this.messageContextKeyService); - const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree')); + const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree.testing-stdtree')); const tree = this._register(this.instantiationService.createInstance( OutputPeekTree, treeContainer, diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 2febdc0a89c..5e84db11b6a 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -1526,8 +1526,8 @@ class TestItemRenderer extends Disposable /** * @inheritdoc */ - public renderTemplate(container: HTMLElement): ITestElementTemplateData { - const wrapper = dom.append(container, dom.$('.test-item')); + public renderTemplate(wrapper: HTMLElement): ITestElementTemplateData { + wrapper.classList.add('testing-stdtree-container'); const icon = dom.append(wrapper, dom.$('.computed-state')); const label = dom.append(wrapper, dom.$('.label')); From 2a4ed8474889817be7266f7e675892fbb055195d Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 18 Dec 2024 00:39:13 +0100 Subject: [PATCH 335/479] Ensure settings and keybindings views open at correct location (#236412) * make sure settings and keybindings views open at correct location * fix tests --- .../browser/preferences.contribution.ts | 54 ++++++++++++------- .../preferences/browser/settingsEditor2.ts | 2 +- .../preferences/browser/preferencesService.ts | 41 ++++++++------ .../preferences/common/preferences.ts | 7 ++- .../test/browser/preferencesService.test.ts | 38 ++++++------- 5 files changed, 87 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 175e8bd518b..2a6357e15d4 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -47,6 +47,9 @@ import { IUserDataProfileService, CURRENT_PROFILE_CONTEXT } from '../../../servi import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +import { resolveCommandsContext } from '../../../browser/parts/editor/editorCommandsContext.js'; +import { IEditorGroup, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; +import { IListService } from '../../../../platform/list/browser/listService.js'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -791,9 +794,10 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ] }); } - run(accessor: ServicesAccessor, args: string | undefined) { - const query = typeof args === 'string' ? args : undefined; - return accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query }); + run(accessor: ServicesAccessor, ...args: unknown[]) { + const query = typeof args[0] === 'string' ? args[0] : undefined; + const groupId = getEditorGroupFromArguments(accessor, args)?.id; + return accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query, groupId }); } })); this._register(MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { @@ -834,8 +838,9 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ] }); } - run(accessor: ServicesAccessor) { - return accessor.get(IPreferencesService).openGlobalKeybindingSettings(true); + run(accessor: ServicesAccessor, ...args: unknown[]) { + const groupId = getEditorGroupFromArguments(accessor, args)?.id; + return accessor.get(IPreferencesService).openGlobalKeybindingSettings(true, { groupId }); } })); this._register(registerAction2(class extends Action2 { @@ -852,8 +857,9 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ] }); } - run(accessor: ServicesAccessor) { - const editorPane = accessor.get(IEditorService).activeEditorPane; + run(accessor: ServicesAccessor, ...args: unknown[]) { + const group = getEditorGroupFromArguments(accessor, args); + const editorPane = group?.activeEditorPane; if (editorPane instanceof KeybindingsEditor) { editorPane.search('@source:system'); } @@ -873,8 +879,9 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ] }); } - run(accessor: ServicesAccessor) { - const editorPane = accessor.get(IEditorService).activeEditorPane; + run(accessor: ServicesAccessor, ...args: unknown[]) { + const group = getEditorGroupFromArguments(accessor, args); + const editorPane = group?.activeEditorPane; if (editorPane instanceof KeybindingsEditor) { editorPane.search('@source:extension'); } @@ -894,8 +901,9 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon ] }); } - run(accessor: ServicesAccessor) { - const editorPane = accessor.get(IEditorService).activeEditorPane; + run(accessor: ServicesAccessor, args: unknown[]) { + const group = getEditorGroupFromArguments(accessor, args); + const editorPane = group?.activeEditorPane; if (editorPane instanceof KeybindingsEditor) { editorPane.search('@source:user'); } @@ -1207,11 +1215,12 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon for (const folder of this.workspaceContextService.getWorkspace().folders) { const commandId = `_workbench.openFolderSettings.${folder.uri.toString()}`; if (!CommandsRegistry.getCommand(commandId)) { - CommandsRegistry.registerCommand(commandId, () => { + CommandsRegistry.registerCommand(commandId, (accessor: ServicesAccessor, ...args: any[]) => { + const groupId = getEditorGroupFromArguments(accessor, args)?.id; if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) { - return this.preferencesService.openWorkspaceSettings({ jsonEditor: false }); + return this.preferencesService.openWorkspaceSettings({ jsonEditor: false, groupId }); } else { - return this.preferencesService.openFolderSettings({ folderUri: folder.uri, jsonEditor: false }); + return this.preferencesService.openFolderSettings({ folderUri: folder.uri, jsonEditor: false, groupId }); } }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { @@ -1261,9 +1270,10 @@ class SettingsEditorTitleContribution extends Disposable implements IWorkbenchCo }] }); } - run(accessor: ServicesAccessor, args: IOpenSettingsActionOptions) { - args = sanitizeOpenSettingsArgs(args); - return accessor.get(IPreferencesService).openUserSettings({ jsonEditor: false, ...args }); + run(accessor: ServicesAccessor, ...args: unknown[]) { + const sanatizedArgs = sanitizeOpenSettingsArgs(args[0]); + const groupId = getEditorGroupFromArguments(accessor, args)?.id; + return accessor.get(IPreferencesService).openUserSettings({ jsonEditor: false, ...sanatizedArgs, groupId }); } }); }; @@ -1289,8 +1299,9 @@ class SettingsEditorTitleContribution extends Disposable implements IWorkbenchCo }] }); } - run(accessor: ServicesAccessor) { - const editorPane = accessor.get(IEditorService).activeEditorPane; + run(accessor: ServicesAccessor, ...args: unknown[]) { + const group = getEditorGroupFromArguments(accessor, args); + const editorPane = group?.activeEditorPane; if (editorPane instanceof SettingsEditor2) { return editorPane.switchToSettingsFile(); } @@ -1300,6 +1311,11 @@ class SettingsEditorTitleContribution extends Disposable implements IWorkbenchCo } } +function getEditorGroupFromArguments(accessor: ServicesAccessor, args: unknown[]): IEditorGroup | undefined { + const context = resolveCommandsContext(args, accessor.get(IEditorService), accessor.get(IEditorGroupsService), accessor.get(IListService)); + return context.groupedEditors[0]?.group; +} + const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); registerWorkbenchContribution2(PreferencesActionsContribution.ID, PreferencesActionsContribution, WorkbenchPhase.BlockStartup); registerWorkbenchContribution2(PreferencesContribution.ID, PreferencesContribution, WorkbenchPhase.BlockStartup); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index c1d1c302db1..8e3388a7008 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -789,7 +789,7 @@ export class SettingsEditor2 extends EditorPane { private async openSettingsFile(options?: ISettingsEditorOptions): Promise { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; - const openOptions: IOpenSettingsOptions = { jsonEditor: true, ...options }; + const openOptions: IOpenSettingsOptions = { jsonEditor: true, groupId: this.group.id, ...options }; if (currentSettingsTarget === ConfigurationTarget.USER_LOCAL) { if (options?.revealSetting) { const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties(); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 025cc02e964..f0bb2cb9f25 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -29,10 +29,10 @@ import { DEFAULT_EDITOR_ASSOCIATION, IEditorPane } from '../../../common/editor. import { EditorInput } from '../../../common/editor/editorInput.js'; import { SideBySideEditorInput } from '../../../common/editor/sideBySideEditorInput.js'; import { IJSONEditingService } from '../../configuration/common/jsonEditing.js'; -import { GroupDirection, IEditorGroupsService } from '../../editor/common/editorGroupsService.js'; -import { IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from '../../editor/common/editorService.js'; +import { GroupDirection, IEditorGroup, IEditorGroupsService } from '../../editor/common/editorGroupsService.js'; +import { IEditorService, SIDE_GROUP } from '../../editor/common/editorService.js'; import { KeybindingsEditorInput } from './keybindingsEditorInput.js'; -import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IKeybindingsEditorOptions, IKeybindingsEditorPane, IOpenSettingsOptions, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorOptions, ISettingsGroup, SETTINGS_AUTHORITY, USE_SPLIT_JSON_SETTING, validateSettingsEditorOptions } from '../common/preferences.js'; +import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IKeybindingsEditorPane, IOpenKeybindingsEditorOptions, IOpenSettingsOptions, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorOptions, ISettingsGroup, SETTINGS_AUTHORITY, USE_SPLIT_JSON_SETTING, validateSettingsEditorOptions } from '../common/preferences.js'; import { SettingsEditor2Input } from '../common/preferencesEditorInput.js'; import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultRawSettingsEditorModel, DefaultSettings, DefaultSettingsEditorModel, Settings2EditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel } from '../common/preferencesModels.js'; import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js'; @@ -48,6 +48,7 @@ import { IURLService } from '../../../../platform/url/common/url.js'; import { compareIgnoreCase } from '../../../../base/common/strings.js'; import { IExtensionService } from '../../extensions/common/extensions.js'; import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; +import { findGroup } from '../../editor/common/editorGroupFinder.js'; const emptyEditableSettingsContent = '{\n}'; @@ -248,8 +249,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic ...options, focusSearch: true }; - await this.editorService.openEditor(input, validateSettingsEditorOptions(options), options.openToSide ? SIDE_GROUP : undefined); - return this.editorGroupService.activeGroup.activeEditorPane!; + const group = await this.getEditorGroupFromOptions(options); + return (await group.openEditor(input, validateSettingsEditorOptions(options)))!; } openApplicationSettings(options: IOpenSettingsOptions = {}): Promise { @@ -312,7 +313,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.open(folderSettingsUri, options); } - async openGlobalKeybindingSettings(textual: boolean, options?: IKeybindingsEditorOptions): Promise { + async openGlobalKeybindingSettings(textual: boolean, options?: IOpenKeybindingsEditorOptions): Promise { options = { pinned: true, revealIfOpened: true, ...options }; if (textual) { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; @@ -322,18 +323,18 @@ export class PreferencesService extends Disposable implements IPreferencesServic // Create as needed and open in editor await this.createIfNotExists(editableKeybindings, emptyContents); if (openDefaultKeybindings) { - const activeEditorGroup = this.editorGroupService.activeGroup; - const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT); + const sourceGroupId = options.groupId ?? this.editorGroupService.activeGroup.id; + const sideEditorGroup = this.editorGroupService.addGroup(sourceGroupId, GroupDirection.RIGHT); await Promise.all([ - this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true, override: DEFAULT_EDITOR_ASSOCIATION.id }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }), + this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true, override: DEFAULT_EDITOR_ASSOCIATION.id }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }, sourceGroupId), this.editorService.openEditor({ resource: editableKeybindings, options }, sideEditorGroup.id) ]); } else { - await this.editorService.openEditor({ resource: editableKeybindings, options }); + await this.editorService.openEditor({ resource: editableKeybindings, options }, options.groupId); } } else { - const editor = (await this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { ...options })) as IKeybindingsEditorPane; + const editor = (await this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { ...options }, options.groupId)) as IKeybindingsEditorPane; if (options.query) { editor.search(options.query); } @@ -345,8 +346,16 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") }); } + private async getEditorGroupFromOptions(options: IOpenSettingsOptions): Promise { + let group = options?.groupId !== undefined ? this.editorGroupService.getGroup(options.groupId) ?? this.editorGroupService.activeGroup : this.editorGroupService.activeGroup; + if (options.openToSide) { + group = (await this.instantiationService.invokeFunction(findGroup, {}, SIDE_GROUP))[0]; + } + return group; + } + private async openSettingsJson(resource: URI, options: IOpenSettingsOptions): Promise { - const group = options?.openToSide ? SIDE_GROUP : undefined; + const group = await this.getEditorGroupFromOptions(options); const editor = await this.doOpenSettingsJson(resource, options, group); if (editor && options?.revealSetting) { await this.revealSetting(options.revealSetting.key, !!options.revealSetting.edit, editor, resource); @@ -354,7 +363,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return editor; } - private async doOpenSettingsJson(resource: URI, options: ISettingsEditorOptions, group?: SIDE_GROUP_TYPE): Promise { + private async doOpenSettingsJson(resource: URI, options: ISettingsEditorOptions, group: IEditorGroup): Promise { const openSplitJSON = !!this.configurationService.getValue(USE_SPLIT_JSON_SETTING); const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING); if (openSplitJSON || openDefaultSettings) { @@ -364,15 +373,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic const configurationTarget = options?.target ?? ConfigurationTarget.USER; const editableSettingsEditorInput = await this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource); options = { ...options, pinned: true }; - return await this.editorService.openEditor(editableSettingsEditorInput, validateSettingsEditorOptions(options), group); + return await group.openEditor(editableSettingsEditorInput, { ...validateSettingsEditorOptions(options) }); } - private async doOpenSplitJSON(resource: URI, options: ISettingsEditorOptions = {}, group?: SIDE_GROUP_TYPE): Promise { + private async doOpenSplitJSON(resource: URI, options: ISettingsEditorOptions = {}, group: IEditorGroup,): Promise { const configurationTarget = options.target ?? ConfigurationTarget.USER; await this.createSettingsIfNotExists(configurationTarget, resource); const preferencesEditorInput = this.createSplitJsonEditorInput(configurationTarget, resource); options = { ...options, pinned: true }; - return this.editorService.openEditor(preferencesEditorInput, validateSettingsEditorOptions(options), group); + return group.openEditor(preferencesEditorInput, validateSettingsEditorOptions(options)); } public createSplitJsonEditorInput(configurationTarget: ConfigurationTarget, resource: URI): EditorInput { diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index e4ae6416bbb..ea385543252 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -211,6 +211,7 @@ export interface ISettingsEditorOptions extends IEditorOptions { export interface IOpenSettingsOptions extends ISettingsEditorOptions { jsonEditor?: boolean; openToSide?: boolean; + groupId?: number; } export function validateSettingsEditorOptions(options: ISettingsEditorOptions): ISettingsEditorOptions { @@ -231,6 +232,10 @@ export interface IKeybindingsEditorOptions extends IEditorOptions { query?: string; } +export interface IOpenKeybindingsEditorOptions extends IKeybindingsEditorOptions { + groupId?: number; +} + export const IPreferencesService = createDecorator('preferencesService'); export interface IPreferencesService { @@ -254,7 +259,7 @@ export interface IPreferencesService { openRemoteSettings(options?: IOpenSettingsOptions): Promise; openWorkspaceSettings(options?: IOpenSettingsOptions): Promise; openFolderSettings(options: IOpenSettingsOptions & { folderUri: IOpenSettingsOptions['folderUri'] }): Promise; - openGlobalKeybindingSettings(textual: boolean, options?: IKeybindingsEditorOptions): Promise; + openGlobalKeybindingSettings(textual: boolean, options?: IOpenKeybindingsEditorOptions): Promise; openDefaultKeybindingsFile(): Promise; openLanguageSpecificSettings(languageId: string, options?: IOpenSettingsOptions): Promise; getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): Promise; diff --git a/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts b/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts index 4f1f2493cb4..77c36590b8a 100644 --- a/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts +++ b/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts @@ -10,26 +10,37 @@ import { ICommandService } from '../../../../../platform/commands/common/command import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; import { IURLService } from '../../../../../platform/url/common/url.js'; -import { DEFAULT_EDITOR_ASSOCIATION } from '../../../../common/editor.js'; +import { DEFAULT_EDITOR_ASSOCIATION, IEditorPane } from '../../../../common/editor.js'; import { IJSONEditingService } from '../../../configuration/common/jsonEditing.js'; import { TestJSONEditingService } from '../../../configuration/test/common/testServices.js'; import { PreferencesService } from '../../browser/preferencesService.js'; import { IPreferencesService, ISettingsEditorOptions } from '../../common/preferences.js'; import { IRemoteAgentService } from '../../../remote/common/remoteAgentService.js'; -import { TestRemoteAgentService, ITestInstantiationService, TestEditorService, workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js'; +import { TestRemoteAgentService, ITestInstantiationService, workbenchInstantiationService, TestEditorGroupView, TestEditorGroupsService } from '../../../../test/browser/workbenchTestServices.js'; +import { IEditorGroupsService } from '../../../editor/common/editorGroupsService.js'; +import { IEditorOptions } from '../../../../../platform/editor/common/editor.js'; +import { SettingsEditor2Input } from '../../common/preferencesEditorInput.js'; suite('PreferencesService', () => { let testInstantiationService: ITestInstantiationService; let testObject: PreferencesService; - let editorService: TestEditorService2; + let lastOpenEditorOptions: IEditorOptions | undefined; const disposables = ensureNoDisposablesAreLeakedInTestSuite(); setup(() => { - editorService = disposables.add(new TestEditorService2()); - testInstantiationService = workbenchInstantiationService({ - editorService: () => editorService - }, disposables); + testInstantiationService = workbenchInstantiationService({}, disposables); + class TestOpenEditorGroupView extends TestEditorGroupView { + lastOpenEditorOptions: any; + override openEditor(_editor: SettingsEditor2Input, options?: IEditorOptions): Promise { + lastOpenEditorOptions = options; + _editor.dispose(); + return Promise.resolve(undefined!); + } + } + + const testEditorGroupService = new TestEditorGroupsService([new TestOpenEditorGroupView(0)]); + testInstantiationService.stub(IEditorGroupsService, testEditorGroupService); testInstantiationService.stub(IJSONEditingService, TestJSONEditingService); testInstantiationService.stub(IRemoteAgentService, TestRemoteAgentService); testInstantiationService.stub(ICommandService, TestCommandService); @@ -42,19 +53,10 @@ suite('PreferencesService', () => { testObject = disposables.add(instantiationService.createInstance(PreferencesService)); }); test('options are preserved when calling openEditor', async () => { - testObject.openSettings({ jsonEditor: false, query: 'test query' }); - const options = editorService.lastOpenEditorOptions as ISettingsEditorOptions; + await testObject.openSettings({ jsonEditor: false, query: 'test query' }); + const options = lastOpenEditorOptions as ISettingsEditorOptions; assert.strictEqual(options.focusSearch, true); assert.strictEqual(options.override, DEFAULT_EDITOR_ASSOCIATION.id); assert.strictEqual(options.query, 'test query'); }); }); - -class TestEditorService2 extends TestEditorService { - lastOpenEditorOptions: any; - - override async openEditor(editorInput: any, options?: any): Promise { - this.lastOpenEditorOptions = options; - return super.openEditor(editorInput, options); - } -} From 3e86f1e6cd9bf88b1ce23192fd0b68380bfc69be Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 17 Dec 2024 16:38:47 -0800 Subject: [PATCH 336/479] cli: fix serve-web needs to wait a certain amount of time after machine startup (#236427) Fixes #233155 --- cli/src/commands/serve_web.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs index 3dedf4b3f05..ddef3eef342 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -548,9 +548,11 @@ impl ConnectionManager { Err(_) => Quality::Stable, }); + let now = Instant::now(); let latest_version = tokio::sync::Mutex::new(cache.get().first().map(|latest_commit| { ( - Instant::now() - Duration::from_secs(RELEASE_CHECK_INTERVAL), + now.checked_sub(Duration::from_secs(RELEASE_CHECK_INTERVAL)) + .unwrap_or(now), // handle 0-ish instants, #233155 Release { name: String::from("0.0.0"), // Version information not stored on cache commit: latest_commit.clone(), From 20899216df0283a9e29f1e45badeb3a8714e6b99 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 07:26:26 +0100 Subject: [PATCH 337/479] dialogs - add `Cmd+D` handling (fix #71430) (#236434) --- src/vs/base/browser/ui/dialog/dialog.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 5fcebbd34c6..2ed8d24fadf 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -275,6 +275,22 @@ export class Dialog extends Disposable { return; // leave default handling } + // Cmd+D (trigger the "no"/"do not save"-button) (macOS only) + if (isMacintosh && evt.equals(KeyMod.CtrlCmd | KeyCode.KeyD)) { + EventHelper.stop(e); + + const noButton = buttonMap.find(button => button.index === 1 && button.index !== this.options.cancelId); + if (noButton) { + resolve({ + button: noButton.index, + checkboxChecked: this.checkbox ? this.checkbox.checked : undefined, + values: this.inputs.length > 0 ? this.inputs.map(input => input.value) : undefined + }); + } + + return; // leave default handling + } + if (evt.equals(KeyCode.Space)) { return; // leave default handling } From 95d0e288d11737685fc91eb083bdc2abb8cd6d78 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 07:43:11 +0100 Subject: [PATCH 338/479] fuzzy - add test coverage for path matching (#236435) --- src/vs/base/test/common/fuzzyScorer.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index b180df4422b..dc30534270f 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -1081,6 +1081,21 @@ suite('Fuzzy Scorer', () => { } }); + test('compareFilesByScore - skip preference on label match when using path sep', function () { + const resourceA = URI.file('djangosite/ufrela/def.py'); + const resourceB = URI.file('djangosite/urls/default.py'); + + for (const query of ['url/def']) { + let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.strictEqual(res[0], resourceB); + assert.strictEqual(res[1], resourceA); + + res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.strictEqual(res[0], resourceB); + assert.strictEqual(res[1], resourceA); + } + }); + test('compareFilesByScore - boost shorter prefix match if multiple queries are used (#99171)', function () { const resourceA = URI.file('mesh_editor_lifetime_job.h'); const resourceB = URI.file('lifetime_job.h'); From cf2ebd91b8e42e3bc5ab0e85e3323c886a977ffe Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 07:45:55 +0100 Subject: [PATCH 339/479] Revert "debt: clean up obsolete file usage" (#236433) Revert "debt: clean up obsolete file usage (#236379)" This reverts commit 625bae23758002b62954fd10b53d25409421d718. --- .../common/extensionManagement.ts | 4 +- .../common/extensionsScannerService.ts | 51 ++++-- .../node/extensionManagementService.ts | 163 +++++++++--------- .../node/extensionsWatcher.ts | 20 +-- .../node/extensionsScannerService.test.ts | 33 +++- 5 files changed, 156 insertions(+), 115 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index f08b46ae65b..155079831fc 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -456,8 +456,8 @@ export const enum ExtensionManagementErrorCode { Extract = 'Extract', Scanning = 'Scanning', ScanningExtension = 'ScanningExtension', - ReadRemoved = 'ReadRemoved', - UnsetRemoved = 'UnsetRemoved', + ReadUninstalled = 'ReadUninstalled', + UnsetUninstalled = 'UnsetUninstalled', Delete = 'Delete', Rename = 'Rename', IntializeDefaultProfile = 'IntializeDefaultProfile', diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index a186b6fa045..99868cddb5d 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -7,6 +7,7 @@ import { coalesce } from '../../../base/common/arrays.js'; import { ThrottledDelayer } from '../../../base/common/async.js'; import * as objects from '../../../base/common/objects.js'; import { VSBuffer } from '../../../base/common/buffer.js'; +import { IStringDictionary } from '../../../base/common/collections.js'; import { getErrorMessage } from '../../../base/common/errors.js'; import { getNodeType, parse, ParseError } from '../../../base/common/json.js'; import { getParseErrorMessage } from '../../../base/common/jsonErrorMessages.js'; @@ -17,11 +18,12 @@ import * as platform from '../../../base/common/platform.js'; import { basename, isEqual, joinPath } from '../../../base/common/resources.js'; import * as semver from '../../../base/common/semver/semver.js'; import Severity from '../../../base/common/severity.js'; +import { isEmptyObject } from '../../../base/common/types.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { IEnvironmentService } from '../../environment/common/environment.js'; import { IProductVersion, Metadata } from './extensionManagement.js'; -import { areSameExtensions, computeTargetPlatform, getExtensionId, getGalleryExtensionId } from './extensionManagementUtil.js'; +import { areSameExtensions, computeTargetPlatform, ExtensionKey, getExtensionId, getGalleryExtensionId } from './extensionManagementUtil.js'; import { ExtensionType, ExtensionIdentifier, IExtensionManifest, TargetPlatform, IExtensionIdentifier, IRelaxedExtensionManifest, UNDEFINED_PUBLISHER, IExtensionDescription, BUILTIN_MANIFEST_CACHE_FILE, USER_MANIFEST_CACHE_FILE, ExtensionIdentifierMap, parseEnabledApiProposalNames } from '../../extensions/common/extensions.js'; import { validateExtensionManifest } from '../../extensions/common/extensionValidator.js'; import { FileOperationResult, IFileService, toFileOperationResult } from '../../files/common/files.js'; @@ -104,6 +106,7 @@ export type ScanOptions = { readonly profileLocation?: URI; readonly includeInvalid?: boolean; readonly includeAllVersions?: boolean; + readonly includeUninstalled?: boolean; readonly checkControlFile?: boolean; readonly language?: string; readonly useCache?: boolean; @@ -142,9 +145,10 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private readonly _onDidChangeCache = this._register(new Emitter()); readonly onDidChangeCache = this._onDidChangeCache.event; - private readonly systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile)); - private readonly userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile)); - private readonly extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner)); + private readonly obsoleteFile = joinPath(this.userExtensionsLocation, '.obsolete'); + private readonly systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile)); + private readonly userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile)); + private readonly extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner, this.obsoleteFile)); constructor( readonly systemExtensionsLocation: URI, @@ -195,8 +199,8 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem const location = scanOptions.profileLocation ?? this.userExtensionsLocation; this.logService.trace('Started scanning user extensions', location); const profileScanOptions: IProfileExtensionsScanOptions | undefined = this.uriIdentityService.extUri.isEqual(scanOptions.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource) ? { bailOutWhenFileNotFound: true } : undefined; - const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion()); - const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode ? this.userExtensionsCachedScanner : this.extensionsScanner; + const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner; let extensions: IRelaxedScannedExtension[]; try { extensions = await extensionsScanner.scanExtensions(extensionsScannerInput); @@ -217,7 +221,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionDevelopmentLocationURI) { const extensions = (await Promise.all(this.environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file) .map(async extensionDevelopmentLocationURI => { - const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, scanOptions.language, false /* do not validate */, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, true, scanOptions.language, false /* do not validate */, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(input); return extensions.map(extension => { // Override the extension type from the existing extensions @@ -233,7 +237,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extension = await this.extensionsScanner.scanExtension(extensionsScannerInput); if (!extension) { return null; @@ -245,7 +249,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput); return this.applyScanOptions(extensions, extensionType, scanOptions, true); } @@ -401,7 +405,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private async scanDefaultSystemExtensions(useCache: boolean, language: string | undefined): Promise { this.logService.trace('Started scanning system extensions'); - const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, language, true, undefined, this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion()); const extensionsScanner = useCache && !extensionsScannerInput.devMode ? this.systemExtensionsCachedScanner : this.extensionsScanner; const result = await extensionsScanner.scanExtensions(extensionsScannerInput); this.logService.trace('Scanned system extensions:', result.length); @@ -431,7 +435,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem break; } } - const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, language, true, undefined, this.getProductVersion()))))); + const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion()))))); this.logService.trace('Scanned dev system extensions:', result.length); return coalesce(result); } @@ -445,7 +449,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } } - private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise { + private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, excludeObsolete: boolean, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise { const translations = await this.getTranslations(language ?? platform.language); const mtime = await this.getMtime(location); const applicationExtensionsLocation = profile && !this.uriIdentityService.extUri.isEqual(location, this.userDataProfilesService.defaultProfile.extensionsResource) ? this.userDataProfilesService.defaultProfile.extensionsResource : undefined; @@ -458,6 +462,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem profile, profileScanOptions, type, + excludeObsolete, validate, productVersion.version, productVersion.date, @@ -499,6 +504,7 @@ export class ExtensionScannerInput { public readonly profile: boolean, public readonly profileScanOptions: IProfileExtensionsScanOptions | undefined, public readonly type: ExtensionType, + public readonly excludeObsolete: boolean, public readonly validate: boolean, public readonly productVersion: string, public readonly productDate: string | undefined, @@ -528,6 +534,7 @@ export class ExtensionScannerInput { && a.profile === b.profile && objects.equals(a.profileScanOptions, b.profileScanOptions) && a.type === b.type + && a.excludeObsolete === b.excludeObsolete && a.validate === b.validate && a.productVersion === b.productVersion && a.productDate === b.productDate @@ -551,6 +558,7 @@ class ExtensionsScanner extends Disposable { private readonly extensionsEnabledWithApiProposalVersion: string[]; constructor( + private readonly obsoleteFile: URI, @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService, @IFileService protected readonly fileService: IFileService, @@ -563,9 +571,15 @@ class ExtensionsScanner extends Disposable { } async scanExtensions(input: ExtensionScannerInput): Promise { - return input.profile - ? this.scanExtensionsFromProfile(input) - : this.scanExtensionsFromLocation(input); + const extensions = input.profile ? await this.scanExtensionsFromProfile(input) : await this.scanExtensionsFromLocation(input); + let obsolete: IStringDictionary = {}; + if (input.excludeObsolete && input.type === ExtensionType.User) { + try { + const raw = (await this.fileService.readFile(this.obsoleteFile)).value.toString(); + obsolete = JSON.parse(raw); + } catch (error) { /* ignore */ } + } + return isEmptyObject(obsolete) ? extensions : extensions.filter(e => !obsolete[ExtensionKey.create(e).toString()]); } private async scanExtensionsFromLocation(input: ExtensionScannerInput): Promise { @@ -582,7 +596,7 @@ class ExtensionsScanner extends Disposable { if (input.type === ExtensionType.User && basename(c.resource).indexOf('.') === 0) { return null; } - const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); return this.scanExtension(extensionScannerInput); })); return coalesce(extensions) @@ -608,7 +622,7 @@ class ExtensionsScanner extends Disposable { const extensions = await Promise.all( scannedProfileExtensions.map(async extensionInfo => { if (filter(extensionInfo)) { - const extensionScannerInput = new ExtensionScannerInput(extensionInfo.location, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + const extensionScannerInput = new ExtensionScannerInput(extensionInfo.location, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); return this.scanExtension(extensionScannerInput, extensionInfo.metadata); } return null; @@ -877,6 +891,7 @@ class CachedExtensionsScanner extends ExtensionsScanner { constructor( private readonly currentProfile: IUserDataProfile, + obsoleteFile: URI, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IUriIdentityService uriIdentityService: IUriIdentityService, @@ -885,7 +900,7 @@ class CachedExtensionsScanner extends ExtensionsScanner { @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService ) { - super(extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService); + super(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService); } override async scanExtensions(input: ExtensionScannerInput): Promise { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 015c3dff393..92405eefb75 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -60,7 +60,7 @@ export interface INativeServerExtensionManagementService extends IExtensionManag readonly _serviceBrand: undefined; scanAllUserInstalledExtensions(): Promise; scanInstalledExtensionAtLocation(location: URI): Promise; - deleteExtensions(...extensions: IExtension[]): Promise; + markAsUninstalled(...extensions: IExtension[]): Promise; } type ExtractExtensionResult = { readonly local: ILocalExtension; readonly verificationStatus?: ExtensionSignatureVerificationCode }; @@ -222,8 +222,8 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return this.extensionsScanner.copyExtensions(fromProfileLocation, toProfileLocation, { version: this.productService.version, date: this.productService.date }); } - deleteExtensions(...extensions: IExtension[]): Promise { - return this.extensionsScanner.setExtensionsForRemoval(...extensions); + markAsUninstalled(...extensions: IExtension[]): Promise { + return this.extensionsScanner.setUninstalled(...extensions); } async cleanUp(): Promise { @@ -480,20 +480,8 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi continue; } - // Ignore changes to the deleted folder - if (this.uriIdentityService.extUri.basename(resource).endsWith(DELETED_FOLDER_POSTFIX)) { - continue; - } - - try { - // Check if this is a directory - if (!(await this.fileService.stat(resource)).isDirectory) { - continue; - } - } catch (error) { - if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { - this.logService.error(error); - } + // Check if this is a directory + if (!(await this.fileService.stat(resource)).isDirectory) { continue; } @@ -514,10 +502,23 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi private async addExtensionsToProfile(extensions: [ILocalExtension, Metadata | undefined][], profileLocation: URI): Promise { const localExtensions = extensions.map(e => e[0]); - await this.extensionsScanner.unsetExtensionsForRemoval(...localExtensions.map(extension => ExtensionKey.create(extension))); + await this.setInstalled(localExtensions); await this.extensionsProfileScannerService.addExtensionsToProfile(extensions, profileLocation); this._onDidInstallExtensions.fire(localExtensions.map(local => ({ local, identifier: local.identifier, operation: InstallOperation.None, profileLocation }))); } + + private async setInstalled(extensions: ILocalExtension[]): Promise { + const uninstalled = await this.extensionsScanner.getUninstalledExtensions(); + for (const extension of extensions) { + const extensionKey = ExtensionKey.create(extension); + if (!uninstalled[extensionKey.toString()]) { + continue; + } + this.logService.trace('Removing the extension from uninstalled list:', extensionKey.id); + await this.extensionsScanner.setInstalled(extensionKey); + this.logService.info('Removed the extension from uninstalled list:', extensionKey.id); + } + } } type UpdateMetadataErrorClassification = { @@ -535,8 +536,8 @@ type UpdateMetadataErrorEvent = { export class ExtensionsScanner extends Disposable { - private readonly obsoletedResource: URI; - private readonly obsoleteFileLimiter: Queue; + private readonly uninstalledResource: URI; + private readonly uninstalledFileLimiter: Queue; private readonly _onExtract = this._register(new Emitter()); readonly onExtract = this._onExtract.event; @@ -554,13 +555,13 @@ export class ExtensionsScanner extends Disposable { @ILogService private readonly logService: ILogService, ) { super(); - this.obsoletedResource = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete'); - this.obsoleteFileLimiter = new Queue(); + this.uninstalledResource = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete'); + this.uninstalledFileLimiter = new Queue(); } async cleanUp(): Promise { await this.removeTemporarilyDeletedFolders(); - await this.deleteExtensionsMarkedForRemoval(); + await this.removeUninstalledExtensions(); await this.initializeMetadata(); } @@ -719,38 +720,40 @@ export class ExtensionsScanner extends Disposable { return this.scanLocalExtension(local.location, local.type, profileLocation); } - async setExtensionsForRemoval(...extensions: IExtension[]): Promise { + async getUninstalledExtensions(): Promise> { + try { + return await this.withUninstalledExtensions(); + } catch (error) { + throw toExtensionManagementError(error, ExtensionManagementErrorCode.ReadUninstalled); + } + } + + async setUninstalled(...extensions: IExtension[]): Promise { const extensionKeys: ExtensionKey[] = extensions.map(e => ExtensionKey.create(e)); - await this.withRemovedExtensions(removedExtensions => + await this.withUninstalledExtensions(uninstalled => extensionKeys.forEach(extensionKey => { - removedExtensions[extensionKey.toString()] = true; - this.logService.info('Marked extension as removed', extensionKey.toString()); + uninstalled[extensionKey.toString()] = true; + this.logService.info('Marked extension as uninstalled', extensionKey.toString()); })); } - async unsetExtensionsForRemoval(...extensionKeys: ExtensionKey[]): Promise { + async setInstalled(extensionKey: ExtensionKey): Promise { try { - const results: boolean[] = []; - await this.withRemovedExtensions(removedExtensions => - extensionKeys.forEach(extensionKey => { - if (removedExtensions[extensionKey.toString()]) { - results.push(true); - delete removedExtensions[extensionKey.toString()]; - } else { - results.push(false); - } - })); - return results; + await this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionKey.toString()]); } catch (error) { - throw toExtensionManagementError(error, ExtensionManagementErrorCode.UnsetRemoved); + throw toExtensionManagementError(error, ExtensionManagementErrorCode.UnsetUninstalled); } } - async deleteExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { + async removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { if (this.uriIdentityService.extUri.isEqualOrParent(extension.location, this.extensionsScannerService.userExtensionsLocation)) { return this.deleteExtensionFromLocation(extension.identifier.id, extension.location, type); } - await this.unsetExtensionsForRemoval(ExtensionKey.create(extension)); + } + + async removeUninstalledExtension(extension: ILocalExtension | IScannedExtension): Promise { + await this.removeExtension(extension, 'uninstalled'); + await this.withUninstalledExtensions(uninstalled => delete uninstalled[ExtensionKey.create(extension).toString()]); } async copyExtension(extension: ILocalExtension, fromProfileLocation: URI, toProfileLocation: URI, metadata: Partial): Promise { @@ -789,11 +792,11 @@ export class ExtensionsScanner extends Disposable { this.logService.info(`Deleted ${type} extension from disk`, id, location.fsPath); } - private withRemovedExtensions(updateFn?: (removed: IStringDictionary) => void): Promise> { - return this.obsoleteFileLimiter.queue(async () => { + private withUninstalledExtensions(updateFn?: (uninstalled: IStringDictionary) => void): Promise> { + return this.uninstalledFileLimiter.queue(async () => { let raw: string | undefined; try { - const content = await this.fileService.readFile(this.obsoletedResource, 'utf8'); + const content = await this.fileService.readFile(this.uninstalledResource, 'utf8'); raw = content.value.toString(); } catch (error) { if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { @@ -801,23 +804,23 @@ export class ExtensionsScanner extends Disposable { } } - let removed = {}; + let uninstalled = {}; if (raw) { try { - removed = JSON.parse(raw); + uninstalled = JSON.parse(raw); } catch (e) { /* ignore */ } } if (updateFn) { - updateFn(removed); - if (Object.keys(removed).length) { - await this.fileService.writeFile(this.obsoletedResource, VSBuffer.fromString(JSON.stringify(removed))); + updateFn(uninstalled); + if (Object.keys(uninstalled).length) { + await this.fileService.writeFile(this.uninstalledResource, VSBuffer.fromString(JSON.stringify(uninstalled))); } else { - await this.fileService.del(this.obsoletedResource); + await this.fileService.del(this.uninstalledResource); } } - return removed; + return uninstalled; }); } @@ -895,25 +898,19 @@ export class ExtensionsScanner extends Disposable { })); } - private async deleteExtensionsMarkedForRemoval(): Promise { - let removed: IStringDictionary; - try { - removed = await this.withRemovedExtensions(); - } catch (error) { - throw toExtensionManagementError(error, ExtensionManagementErrorCode.ReadRemoved); - } - - if (Object.keys(removed).length === 0) { - this.logService.debug(`No extensions are marked as removed.`); + private async removeUninstalledExtensions(): Promise { + const uninstalled = await this.getUninstalledExtensions(); + if (Object.keys(uninstalled).length === 0) { + this.logService.debug(`No uninstalled extensions found.`); return; } - this.logService.debug(`Deleting extensions marked as removed:`, Object.keys(removed)); + this.logService.debug(`Removing uninstalled extensions:`, Object.keys(uninstalled)); - const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeInvalid: true }); // All user extensions + const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeUninstalled: true, includeInvalid: true }); // All user extensions const installed: Set = new Set(); for (const e of extensions) { - if (!removed[ExtensionKey.create(e).toString()]) { + if (!uninstalled[ExtensionKey.create(e).toString()]) { installed.add(e.identifier.id.toLowerCase()); } } @@ -931,8 +928,8 @@ export class ExtensionsScanner extends Disposable { this.logService.error(error); } - const toRemove = extensions.filter(e => e.metadata /* Installed by System */ && removed[ExtensionKey.create(e).toString()]); - await Promise.allSettled(toRemove.map(e => this.deleteExtension(e, 'marked for removal'))); + const toRemove = extensions.filter(e => e.metadata /* Installed by System */ && uninstalled[ExtensionKey.create(e).toString()]); + await Promise.allSettled(toRemove.map(e => this.removeUninstalledExtension(e))); } private async removeTemporarilyDeletedFolders(): Promise { @@ -1024,7 +1021,7 @@ class InstallExtensionInProfileTask extends AbstractExtensionTask { - // If the same version of extension is marked as removed, remove it from there and return the local. - const [removed] = await this.extensionsScanner.unsetExtensionsForRemoval(extensionKey); - if (removed) { - this.logService.info('Removed the extension from removed list:', extensionKey.id); - const userExtensions = await this.extensionsScanner.scanAllUserExtensions(true); - return userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)); + private async unsetIfUninstalled(extensionKey: ExtensionKey): Promise { + const uninstalled = await this.extensionsScanner.getUninstalledExtensions(); + if (!uninstalled[extensionKey.toString()]) { + return undefined; } - return undefined; + + this.logService.trace('Removing the extension from uninstalled list:', extensionKey.id); + // If the same version of extension is marked as uninstalled, remove it from there and return the local. + await this.extensionsScanner.setInstalled(extensionKey); + this.logService.info('Removed the extension from uninstalled list:', extensionKey.id); + + const userExtensions = await this.extensionsScanner.scanAllUserExtensions(true); + return userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)); } private async updateMetadata(extension: ILocalExtension, token: CancellationToken): Promise { @@ -1148,8 +1149,8 @@ class UninstallExtensionInProfileTask extends AbstractExtensionTask implem super(); } - protected doRun(token: CancellationToken): Promise { - return this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension, this.options.profileLocation); + protected async doRun(token: CancellationToken): Promise { + await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension, this.options.profileLocation); } } diff --git a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts index 2c4e976a5a6..d2b65eaea55 100644 --- a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts +++ b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts @@ -48,7 +48,7 @@ export class ExtensionsWatcher extends Disposable { await this.extensionsScannerService.initializeDefaultProfileExtensions(); await this.onDidChangeProfiles(this.userDataProfilesService.profiles); this.registerListeners(); - await this.deleteExtensionsNotInProfiles(); + await this.uninstallExtensionsNotInProfiles(); } private registerListeners(): void { @@ -102,7 +102,7 @@ export class ExtensionsWatcher extends Disposable { } private async onDidRemoveExtensions(e: DidRemoveProfileExtensionsEvent): Promise { - const extensionsToDelete: IExtension[] = []; + const extensionsToUninstall: IExtension[] = []; const promises: Promise[] = []; for (const extension of e.extensions) { const key = this.getKey(extension.identifier, extension.version); @@ -115,7 +115,7 @@ export class ExtensionsWatcher extends Disposable { promises.push(this.extensionManagementService.scanInstalledExtensionAtLocation(extension.location) .then(result => { if (result) { - extensionsToDelete.push(result); + extensionsToUninstall.push(result); } else { this.logService.info('Extension not found at the location', extension.location.toString()); } @@ -125,8 +125,8 @@ export class ExtensionsWatcher extends Disposable { } try { await Promise.all(promises); - if (extensionsToDelete.length) { - await this.deleteExtensionsNotInProfiles(extensionsToDelete); + if (extensionsToUninstall.length) { + await this.uninstallExtensionsNotInProfiles(extensionsToUninstall); } } catch (error) { this.logService.error(error); @@ -180,13 +180,13 @@ export class ExtensionsWatcher extends Disposable { } } - private async deleteExtensionsNotInProfiles(toDelete?: IExtension[]): Promise { - if (!toDelete) { + private async uninstallExtensionsNotInProfiles(toUninstall?: IExtension[]): Promise { + if (!toUninstall) { const installed = await this.extensionManagementService.scanAllUserInstalledExtensions(); - toDelete = installed.filter(installedExtension => !this.allExtensions.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); + toUninstall = installed.filter(installedExtension => !this.allExtensions.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); } - if (toDelete.length) { - await this.extensionManagementService.deleteExtensions(...toDelete); + if (toUninstall.length) { + await this.extensionManagementService.markAsUninstalled(...toUninstall); } } diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts index 551ba576d44..74d3ffcd738 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts @@ -224,6 +224,31 @@ suite('NativeExtensionsScanerService Test', () => { assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); }); + test('scan exclude uninstalled extensions', async () => { + await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); + await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub' })); + await instantiationService.get(IFileService).writeFile(joinPath(URI.file(instantiationService.get(INativeEnvironmentService).extensionsPath), '.obsolete'), VSBuffer.fromString(JSON.stringify({ 'pub.name2-1.0.0': true }))); + const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); + + const actual = await testObject.scanUserExtensions({}); + + assert.deepStrictEqual(actual.length, 1); + assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); + }); + + test('scan include uninstalled extensions', async () => { + await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); + await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub' })); + await instantiationService.get(IFileService).writeFile(joinPath(URI.file(instantiationService.get(INativeEnvironmentService).extensionsPath), '.obsolete'), VSBuffer.fromString(JSON.stringify({ 'pub.name2-1.0.0': true }))); + const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); + + const actual = await testObject.scanUserExtensions({ includeUninstalled: true }); + + assert.deepStrictEqual(actual.length, 2); + assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); + assert.deepStrictEqual(actual[1].identifier, { id: 'pub.name2' }); + }); + test('scan include invalid extensions', async () => { await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub', engines: { vscode: '^1.67.0' } })); @@ -326,7 +351,7 @@ suite('ExtensionScannerInput', () => { ensureNoDisposablesAreLeakedInTestSuite(); test('compare inputs - location', () => { - const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(location, mtime, undefined, undefined, false, undefined, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(location, mtime, undefined, undefined, false, undefined, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, undefined), anInput(ROOT, undefined)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, 100), anInput(ROOT, 100)), true); @@ -336,7 +361,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - application location', () => { - const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(ROOT, undefined, location, mtime, false, undefined, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(ROOT, undefined, location, mtime, false, undefined, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, undefined), anInput(ROOT, undefined)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, 100), anInput(ROOT, 100)), true); @@ -346,7 +371,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - profile', () => { - const anInput = (profile: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, profile, profileScanOptions, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (profile: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, profile, profileScanOptions, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(true, { bailOutWhenFileNotFound: true }), anInput(true, { bailOutWhenFileNotFound: true })), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(false, { bailOutWhenFileNotFound: true }), anInput(false, { bailOutWhenFileNotFound: true })), true); @@ -359,7 +384,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - extension type', () => { - const anInput = (type: ExtensionType) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, false, undefined, type, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (type: ExtensionType) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, false, undefined, type, true, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ExtensionType.System), anInput(ExtensionType.System)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ExtensionType.User), anInput(ExtensionType.User)), true); From 83f695ad2ae4befe613dfd1bb1b1d8547d647d53 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:29:54 +0100 Subject: [PATCH 340/479] Move selected editors instead of only activ ones (#236327) * move selected editors * :lipstick: * :lipstick: --- .../browser/parts/editor/editorActions.ts | 38 +++++++------- .../browser/parts/editor/editorCommands.ts | 52 ++++++++++++------- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index be79b3f0af2..9d9766f4eb5 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -12,7 +12,7 @@ import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser import { GoFilter, IHistoryService } from '../../../services/history/common/history.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID as NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID } from './editorCommands.js'; +import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, SelectedEditorsMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID as NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID } from './editorCommands.js'; import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, MergeGroupMode } from '../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -1996,7 +1996,7 @@ export class MoveEditorLeftInGroupAction extends ExecuteCommandAction { }, f1: true, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'left' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'left' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2015,7 +2015,7 @@ export class MoveEditorRightInGroupAction extends ExecuteCommandAction { }, f1: true, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'right' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'right' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2034,7 +2034,7 @@ export class MoveEditorToPreviousGroupAction extends ExecuteCommandAction { }, f1: true, category: Categories.View, - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'previous', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'previous', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2053,7 +2053,7 @@ export class MoveEditorToNextGroupAction extends ExecuteCommandAction { } }, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'next', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'next', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2065,7 +2065,7 @@ export class MoveEditorToAboveGroupAction extends ExecuteCommandAction { title: localize2('moveEditorToAboveGroup', 'Move Editor into Group Above'), f1: true, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'up', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'up', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2077,7 +2077,7 @@ export class MoveEditorToBelowGroupAction extends ExecuteCommandAction { title: localize2('moveEditorToBelowGroup', 'Move Editor into Group Below'), f1: true, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'down', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'down', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2089,7 +2089,7 @@ export class MoveEditorToLeftGroupAction extends ExecuteCommandAction { title: localize2('moveEditorToLeftGroup', 'Move Editor into Left Group'), f1: true, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'left', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'left', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2101,7 +2101,7 @@ export class MoveEditorToRightGroupAction extends ExecuteCommandAction { title: localize2('moveEditorToRightGroup', 'Move Editor into Right Group'), f1: true, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'right', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'right', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2120,7 +2120,7 @@ export class MoveEditorToFirstGroupAction extends ExecuteCommandAction { } }, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'first', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'first', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2139,7 +2139,7 @@ export class MoveEditorToLastGroupAction extends ExecuteCommandAction { } }, category: Categories.View - }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'last', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, MOVE_ACTIVE_EDITOR_COMMAND_ID, { to: 'last', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2151,7 +2151,7 @@ export class SplitEditorToPreviousGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToPreviousGroup', 'Split Editor into Previous Group'), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'previous', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'previous', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2163,7 +2163,7 @@ export class SplitEditorToNextGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToNextGroup', 'Split Editor into Next Group'), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'next', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'next', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2175,7 +2175,7 @@ export class SplitEditorToAboveGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToAboveGroup', 'Split Editor into Group Above'), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'up', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'up', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2187,7 +2187,7 @@ export class SplitEditorToBelowGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToBelowGroup', 'Split Editor into Group Below'), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'down', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'down', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2202,7 +2202,7 @@ export class SplitEditorToLeftGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToLeftGroup', "Split Editor into Left Group"), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'left', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'left', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2214,7 +2214,7 @@ export class SplitEditorToRightGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToRightGroup', 'Split Editor into Right Group'), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'right', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'right', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2226,7 +2226,7 @@ export class SplitEditorToFirstGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToFirstGroup', 'Split Editor into First Group'), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'first', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'first', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } @@ -2238,7 +2238,7 @@ export class SplitEditorToLastGroupAction extends ExecuteCommandAction { title: localize2('splitEditorToLastGroup', 'Split Editor into Last Group'), f1: true, category: Categories.View - }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'last', by: 'group' } satisfies ActiveEditorMoveCopyArguments); + }, COPY_ACTIVE_EDITOR_COMMAND_ID, { to: 'last', by: 'group' } satisfies SelectedEditorsMoveCopyArguments); } } diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 83f88bd7b74..1bbe6258eb0 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -28,7 +28,7 @@ import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from './editorQuickAc import { SideBySideEditor } from './sideBySideEditor.js'; import { TextDiffEditor } from './textDiffEditor.js'; import { ActiveEditorCanSplitInGroupContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupLockedContext, ActiveEditorStickyContext, MultipleEditorGroupsContext, SideBySideEditorActiveContext, TextCompareEditorActiveContext } from '../../../common/contextkeys.js'; -import { CloseDirection, EditorInputCapabilities, EditorsOrder, IResourceDiffEditorInput, IUntitledTextResourceEditorInput, IVisibleEditorPane, isEditorInputWithOptionsAndGroup } from '../../../common/editor.js'; +import { CloseDirection, EditorInputCapabilities, EditorsOrder, IResourceDiffEditorInput, IUntitledTextResourceEditorInput, isEditorInputWithOptionsAndGroup } from '../../../common/editor.js'; import { DiffEditorInput } from '../../../common/editor/diffEditorInput.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { SideBySideEditorInput } from '../../../common/editor/sideBySideEditorInput.js'; @@ -108,13 +108,13 @@ export const EDITOR_CORE_NAVIGATION_COMMANDS = [ TOGGLE_MAXIMIZE_EDITOR_GROUP ]; -export interface ActiveEditorMoveCopyArguments { +export interface SelectedEditorsMoveCopyArguments { to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next'; by?: 'tab' | 'group'; value?: number; } -const isActiveEditorMoveCopyArg = function (arg: ActiveEditorMoveCopyArguments): boolean { +const isSelectedEditorsMoveCopyArg = function (arg: SelectedEditorsMoveCopyArguments): boolean { if (!isObject(arg)) { return false; } @@ -159,14 +159,14 @@ function registerActiveEditorMoveCopyCommand(): void { weight: KeybindingWeight.WorkbenchContrib, when: EditorContextKeys.editorTextFocus, primary: 0, - handler: (accessor, args) => moveCopyActiveEditor(true, args, accessor), + handler: (accessor, args) => moveCopySelectedEditors(true, args, accessor), metadata: { description: localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"), args: [ { name: localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"), description: localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."), - constraint: isActiveEditorMoveCopyArg, + constraint: isSelectedEditorsMoveCopyArg, schema: moveCopyJSONSchema } ] @@ -178,42 +178,55 @@ function registerActiveEditorMoveCopyCommand(): void { weight: KeybindingWeight.WorkbenchContrib, when: EditorContextKeys.editorTextFocus, primary: 0, - handler: (accessor, args) => moveCopyActiveEditor(false, args, accessor), + handler: (accessor, args) => moveCopySelectedEditors(false, args, accessor), metadata: { description: localize('editorCommand.activeEditorCopy.description', "Copy the active editor by groups"), args: [ { name: localize('editorCommand.activeEditorCopy.arg.name', "Active editor copy argument"), description: localize('editorCommand.activeEditorCopy.arg.description', "Argument Properties:\n\t* 'to': String value providing where to copy.\n\t* 'value': Number value providing how many positions or an absolute position to copy."), - constraint: isActiveEditorMoveCopyArg, + constraint: isSelectedEditorsMoveCopyArg, schema: moveCopyJSONSchema } ] } }); - function moveCopyActiveEditor(isMove: boolean, args: ActiveEditorMoveCopyArguments = Object.create(null), accessor: ServicesAccessor): void { + function moveCopySelectedEditors(isMove: boolean, args: SelectedEditorsMoveCopyArguments = Object.create(null), accessor: ServicesAccessor): void { args.to = args.to || 'right'; args.by = args.by || 'tab'; args.value = typeof args.value === 'number' ? args.value : 1; - const activeEditorPane = accessor.get(IEditorService).activeEditorPane; - if (activeEditorPane) { + const activeGroup = accessor.get(IEditorGroupsService).activeGroup; + const selectedEditors = activeGroup.selectedEditors; + if (selectedEditors.length > 0) { switch (args.by) { case 'tab': if (isMove) { - return moveActiveTab(args, activeEditorPane); + return moveTabs(args, activeGroup, selectedEditors); } break; case 'group': - return moveCopyActiveEditorToGroup(isMove, args, activeEditorPane, accessor); + return moveCopyActiveEditorToGroup(isMove, args, activeGroup, selectedEditors, accessor); } } } - function moveActiveTab(args: ActiveEditorMoveCopyArguments, control: IVisibleEditorPane): void { - const group = control.group; - let index = group.getIndexOfEditor(control.input); + function moveTabs(args: SelectedEditorsMoveCopyArguments, group: IEditorGroup, editors: EditorInput[]): void { + const to = args.to; + if (to === 'first' || to === 'right') { + editors = [...editors].reverse(); + } else if (to === 'position' && (args.value ?? 1) < group.getIndexOfEditor(editors[0])) { + editors = [...editors].reverse(); + } + + for (const editor of editors) { + moveTab(args, group, editor); + } + } + + function moveTab(args: SelectedEditorsMoveCopyArguments, group: IEditorGroup, editor: EditorInput): void { + let index = group.getIndexOfEditor(editor); switch (args.to) { case 'first': index = 0; @@ -236,14 +249,13 @@ function registerActiveEditorMoveCopyCommand(): void { } index = index < 0 ? 0 : index >= group.count ? group.count - 1 : index; - group.moveEditor(control.input, group, { index }); + group.moveEditor(editor, group, { index }); } - function moveCopyActiveEditorToGroup(isMove: boolean, args: ActiveEditorMoveCopyArguments, control: IVisibleEditorPane, accessor: ServicesAccessor): void { + function moveCopyActiveEditorToGroup(isMove: boolean, args: SelectedEditorsMoveCopyArguments, sourceGroup: IEditorGroup, editors: EditorInput[], accessor: ServicesAccessor): void { const editorGroupsService = accessor.get(IEditorGroupsService); const configurationService = accessor.get(IConfigurationService); - const sourceGroup = control.group; let targetGroup: IEditorGroup | undefined; switch (args.to) { @@ -296,9 +308,9 @@ function registerActiveEditorMoveCopyCommand(): void { if (targetGroup) { if (isMove) { - sourceGroup.moveEditor(control.input, targetGroup); + sourceGroup.moveEditors(editors.map(editor => ({ editor })), targetGroup); } else if (sourceGroup.id !== targetGroup.id) { - sourceGroup.copyEditor(control.input, targetGroup); + sourceGroup.copyEditors(editors.map(editor => ({ editor })), targetGroup); } targetGroup.focus(); } From 2fb0656601fdd8ee16e4bda6f4e788594a22e8f2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 10:27:36 +0100 Subject: [PATCH 341/479] Removal of `chat.experimental.offerSetup` (fix microsoft/vscode-copilot#11286) (#236320) --- .../chat/browser/actions/chatActions.ts | 2 - .../contrib/chat/browser/chat.contribution.ts | 7 -- .../browser/chatParticipant.contribution.ts | 5 +- .../contrib/chat/browser/chatSetup.ts | 16 ++-- .../contrib/chat/common/chatContextKeys.ts | 36 ++++---- .../common/gettingStartedContent.ts | 84 ++++++++++--------- 6 files changed, 68 insertions(+), 82 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index de160c8c263..92f6c42dd3d 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -534,7 +534,6 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, { when: ContextKeyExpr.and( ContextKeyExpr.has('config.chat.commandCenter.enabled'), ContextKeyExpr.or( - ContextKeyExpr.has('config.chat.experimental.offerSetup'), ChatContextKeys.Setup.installed, ChatContextKeys.panelParticipantRegistered ) @@ -552,7 +551,6 @@ registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction { ContextKeyExpr.has('config.window.commandCenter'), ContextKeyExpr.or( ChatContextKeys.Setup.installed, - ContextKeyExpr.has('config.chat.experimental.offerSetup'), ChatContextKeys.panelParticipantRegistered ) ) diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 2aa243649de..b03d4605d16 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -121,13 +121,6 @@ configurationRegistry.registerConfiguration({ markdownDescription: nls.localize('chat.commandCenter.enabled', "Controls whether the command center shows a menu for actions to control Copilot (requires {0}).", '`#window.commandCenter#`'), default: true }, - 'chat.experimental.offerSetup': { - type: 'boolean', - default: false, - scope: ConfigurationScope.APPLICATION, - markdownDescription: nls.localize('chat.experimental.offerSetup', "Controls whether setup is offered for Chat if not done already."), - tags: ['experimental', 'onExP'] - }, 'chat.editing.alwaysSaveWithGeneratedChanges': { type: 'boolean', scope: ConfigurationScope.APPLICATION, diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts index 355a3d55ff8..6326c643d9c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts @@ -309,10 +309,7 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { }, ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.Panel }]), when: ContextKeyExpr.or( - ContextKeyExpr.and( - ContextKeyExpr.has('config.chat.experimental.offerSetup'), - ChatContextKeys.Setup.triggered - ), + ChatContextKeys.Setup.triggered, ChatContextKeys.Setup.installed, ChatContextKeys.panelParticipantRegistered, ChatContextKeys.extensionInvalid diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 54e081fbc0d..aab4103ddac 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -132,12 +132,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr private registerActions(): void { const that = this; - const chatSetupTriggerContext = ContextKeyExpr.and( - ContextKeyExpr.has('config.chat.experimental.offerSetup'), - ContextKeyExpr.or( - ChatContextKeys.Setup.installed.negate(), - ChatContextKeys.Setup.canSignUp - ) + const chatSetupTriggerContext = ContextKeyExpr.or( + ChatContextKeys.Setup.installed.negate(), + ChatContextKeys.Setup.canSignUp ); class ChatSetupTriggerAction extends Action2 { @@ -188,10 +185,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr title: ChatSetupHideAction.TITLE, f1: true, category: CHAT_CATEGORY, - precondition: ContextKeyExpr.and( - ChatContextKeys.Setup.installed.negate(), - ContextKeyExpr.has('config.chat.experimental.offerSetup') - ), + precondition: ChatContextKeys.Setup.installed.negate(), menu: { id: MenuId.ChatCommandCenter, group: 'z_hide', @@ -1006,6 +1000,7 @@ class ChatSetupContext extends Disposable { private readonly canSignUpContextKey = ChatContextKeys.Setup.canSignUp.bindTo(this.contextKeyService); private readonly signedOutContextKey = ChatContextKeys.Setup.signedOut.bindTo(this.contextKeyService); private readonly limitedContextKey = ChatContextKeys.Setup.limited.bindTo(this.contextKeyService); + private readonly proContextKey = ChatContextKeys.Setup.pro.bindTo(this.contextKeyService); private readonly triggeredContext = ChatContextKeys.Setup.triggered.bindTo(this.contextKeyService); private readonly installedContext = ChatContextKeys.Setup.installed.bindTo(this.contextKeyService); @@ -1108,6 +1103,7 @@ class ChatSetupContext extends Disposable { this.signedOutContextKey.set(this._state.entitlement === ChatEntitlement.Unknown); this.canSignUpContextKey.set(this._state.entitlement === ChatEntitlement.Available); this.limitedContextKey.set(this._state.entitlement === ChatEntitlement.Limited); + this.proContextKey.set(this._state.entitlement === ChatEntitlement.Pro); this.triggeredContext.set(!!this._state.triggered); this.installedContext.set(!!this._state.installed); diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index 047a5824af2..f615233de50 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -42,31 +42,31 @@ export namespace ChatContextKeys { export const languageModelsAreUserSelectable = new RawContextKey('chatModelsAreUserSelectable', false, { type: 'boolean', description: localize('chatModelsAreUserSelectable', "True when the chat model can be selected manually by the user.") }); export const Setup = { - canSignUp: new RawContextKey('chatSetupCanSignUp', false, true), // True when user can sign up to be a chat limited user. + // State signedOut: new RawContextKey('chatSetupSignedOut', false, true), // True when user is signed out. - limited: new RawContextKey('chatSetupLimited', false, true), // True when user is a chat limited user. - triggered: new RawContextKey('chatSetupTriggered', false, true), // True when chat setup is triggered. installed: new RawContextKey('chatSetupInstalled', false, true), // True when the chat extension is installed. + + // Plans + canSignUp: new RawContextKey('chatPlanCanSignUp', false, true), // True when user can sign up to be a chat limited user. + limited: new RawContextKey('chatPlanLimited', false, true), // True when user is a chat limited user. + pro: new RawContextKey('chatPlanPro', false, true) // True when user is a chat pro user. }; export const SetupViewKeys = new Set([ChatContextKeys.Setup.triggered.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Setup.signedOut.key, ChatContextKeys.Setup.canSignUp.key]); - export const SetupViewCondition = ContextKeyExpr.and( - ContextKeyExpr.has('config.chat.experimental.offerSetup'), - ContextKeyExpr.or( - ContextKeyExpr.and( - ChatContextKeys.Setup.triggered, - ChatContextKeys.Setup.installed.negate() - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.canSignUp, - ChatContextKeys.Setup.installed - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.signedOut, - ChatContextKeys.Setup.installed - ) + export const SetupViewCondition = ContextKeyExpr.or( + ContextKeyExpr.and( + ChatContextKeys.Setup.triggered, + ChatContextKeys.Setup.installed.negate() + ), + ContextKeyExpr.and( + ChatContextKeys.Setup.canSignUp, + ChatContextKeys.Setup.installed + ), + ContextKeyExpr.and( + ChatContextKeys.Setup.signedOut, + ChatContextKeys.Setup.installed ) )!; diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 303905c9136..7fd3e30163f 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -246,9 +246,9 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ content: { type: 'steps', steps: [ - createCopilotSetupStep('CopilotSetupSignedOut', CopilotSignedOutButton, 'config.chat.experimental.offerSetup && chatSetupSignedOut', true), - createCopilotSetupStep('CopilotSetupComplete', CopilotCompleteButton, 'config.chat.experimental.offerSetup && chatSetupInstalled && (chatSetupEntitled || chatSetupLimited)', false), - createCopilotSetupStep('CopilotSetupSignedIn', CopilotSignedInButton, 'config.chat.experimental.offerSetup && !chatSetupSignedOut && (!chatSetupInstalled || chatSetupCanSignUp)', true), + createCopilotSetupStep('CopilotSetupSignedOut', CopilotSignedOutButton, 'chatSetupSignedOut', true), + createCopilotSetupStep('CopilotSetupComplete', CopilotCompleteButton, 'chatSetupInstalled && (chatPlanPro || chatPlanLimited)', false), + createCopilotSetupStep('CopilotSetupSignedIn', CopilotSignedInButton, '!chatSetupSignedOut && (!chatSetupInstalled || chatPlanCanSignUp)', true), { id: 'pickColorTheme', title: localize('gettingStarted.pickColor.title', "Choose your theme"), @@ -277,30 +277,31 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ type: 'svg', altText: 'Language extensions', path: 'languages.svg' }, }, - { - id: 'settings', - title: localize('gettingStarted.settings.title', "Tune your settings"), - description: localize('gettingStarted.settings.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')), - when: '!config.chat.experimental.offerSetup', - media: { - type: 'svg', altText: 'VS Code Settings', path: 'settings.svg' - }, - }, - { - id: 'settingsSync', - title: localize('gettingStarted.settingsSync.title', "Sync settings across devices"), - description: localize('gettingStarted.settingsSync.description.interpolated', "Keep your essential customizations backed up and updated across all your devices.\n{0}", Button(localize('enableSync', "Backup and Sync Settings"), 'command:workbench.userDataSync.actions.turnOn')), - when: '!config.chat.experimental.offerSetup && syncStatus != uninitialized', - completionEvents: ['onEvent:sync-enabled'], - media: { - type: 'svg', altText: 'The "Turn on Sync" entry in the settings gear menu.', path: 'settingsSync.svg' - }, - }, + // Hidden in favor of copilot entry (to be revisited when copilot entry moves, if at all) + // { + // id: 'settings', + // title: localize('gettingStarted.settings.title', "Tune your settings"), + // description: localize('gettingStarted.settings.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')), + // when: '!config.chat.experimental.offerSetup', + // media: { + // type: 'svg', altText: 'VS Code Settings', path: 'settings.svg' + // }, + // }, + // { + // id: 'settingsSync', + // title: localize('gettingStarted.settingsSync.title', "Sync settings across devices"), + // description: localize('gettingStarted.settingsSync.description.interpolated', "Keep your essential customizations backed up and updated across all your devices.\n{0}", Button(localize('enableSync', "Backup and Sync Settings"), 'command:workbench.userDataSync.actions.turnOn')), + // when: '!config.chat.experimental.offerSetup && syncStatus != uninitialized', + // completionEvents: ['onEvent:sync-enabled'], + // media: { + // type: 'svg', altText: 'The "Turn on Sync" entry in the settings gear menu.', path: 'settingsSync.svg' + // }, + // }, { id: 'settingsAndSync', title: localize('gettingStarted.settings.title', "Tune your settings"), description: localize('gettingStarted.settingsAndSync.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. [Back up and sync](command:workbench.userDataSync.actions.turnOn) your essential customizations across all your devices.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')), - when: 'config.chat.experimental.offerSetup && syncStatus != uninitialized', + when: 'syncStatus != uninitialized', completionEvents: ['onEvent:sync-enabled'], media: { type: 'svg', altText: 'VS Code Settings', path: 'settings.svg' @@ -312,24 +313,25 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ description: localize('gettingStarted.commandPalette.description.interpolated', "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", Button(localize('commandPalette', "Open Command Palette"), 'command:workbench.action.showCommands')), media: { type: 'svg', altText: 'Command Palette overlay for searching and executing commands.', path: 'commandPalette.svg' }, }, - { - id: 'pickAFolderTask-Mac', - title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"), - description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFileFolder')), - when: '!config.chat.experimental.offerSetup && isMac && workspaceFolderCount == 0', - media: { - type: 'svg', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: 'openFolder.svg' - } - }, - { - id: 'pickAFolderTask-Other', - title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"), - description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFolder')), - when: '!config.chat.experimental.offerSetup && !isMac && workspaceFolderCount == 0', - media: { - type: 'svg', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: 'openFolder.svg' - } - }, + // Hidden in favor of copilot entry (to be revisited when copilot entry moves, if at all) + // { + // id: 'pickAFolderTask-Mac', + // title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"), + // description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFileFolder')), + // when: '!config.chat.experimental.offerSetup && isMac && workspaceFolderCount == 0', + // media: { + // type: 'svg', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: 'openFolder.svg' + // } + // }, + // { + // id: 'pickAFolderTask-Other', + // title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"), + // description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFolder')), + // when: '!config.chat.experimental.offerSetup && !isMac && workspaceFolderCount == 0', + // media: { + // type: 'svg', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: 'openFolder.svg' + // } + // }, { id: 'quickOpen', title: localize('gettingStarted.quickOpen.title', "Quickly navigate between your files"), From 3daa33e93a945f4f6cab577f424e1862c00846c1 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:31:29 +0100 Subject: [PATCH 342/479] Restore close commands for keybinding support (#236446) * bring back the close commands * improve discoverability --- .../browser/actions/layoutActions.ts | 22 +++---------------- .../parts/auxiliarybar/auxiliaryBarActions.ts | 18 +++++++++++++++ .../browser/parts/panel/panelActions.ts | 18 +++++++++++++++ .../browser/parts/sidebar/sidebarActions.ts | 18 +++++++++++++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index c3ba2ab9907..75253e235de 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -53,25 +53,6 @@ const fullscreenIcon = registerIcon('fullscreen', Codicon.screenFull, localize(' const centerLayoutIcon = registerIcon('centerLayoutIcon', Codicon.layoutCentered, localize('centerLayoutIcon', "Represents centered layout mode")); const zenModeIcon = registerIcon('zenMode', Codicon.target, localize('zenModeIcon', "Represents zen mode")); - -// --- Close Side Bar - -registerAction2(class extends Action2 { - - constructor() { - super({ - id: 'workbench.action.closeSidebar', - title: localize2('closeSidebar', 'Close Primary Side Bar'), - category: Categories.View, - f1: true - }); - } - - run(accessor: ServicesAccessor): void { - accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.SIDEBAR_PART); - } -}); - export const ToggleActivityBarVisibilityActionId = 'workbench.action.toggleActivityBarVisibility'; // --- Toggle Centered Layout @@ -323,6 +304,9 @@ class ToggleSidebarVisibilityAction extends Action2 { title: localize('primary sidebar', "Primary Side Bar"), mnemonicTitle: localize({ key: 'primary sidebar mnemonic', comment: ['&& denotes a mnemonic'] }, "&&Primary Side Bar"), }, + metadata: { + description: localize('openAndCloseSidebar', 'Open/Show and Close/Hide Sidebar'), + }, category: Categories.View, f1: true, keybinding: { diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts index 3bea858332e..e564a8d49d7 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts @@ -41,6 +41,9 @@ export class ToggleAuxiliaryBarAction extends Action2 { }, icon: closeIcon, // Ensures no flickering when using toggled.icon category: Categories.View, + metadata: { + description: localize('openAndCloseAuxiliaryBar', 'Open/Show and Close/Hide Secondary Side Bar'), + }, f1: true, keybinding: { weight: KeybindingWeight.WorkbenchContrib, @@ -74,6 +77,21 @@ export class ToggleAuxiliaryBarAction extends Action2 { registerAction2(ToggleAuxiliaryBarAction); +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.closeAuxiliaryBar', + title: localize2('closeSecondarySideBar', 'Hide Secondary Side Bar'), + category: Categories.View, + precondition: AuxiliaryBarVisibleContext, + f1: true, + }); + } + run(accessor: ServicesAccessor) { + accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.AUXILIARYBAR_PART); + } +}); + registerAction2(class FocusAuxiliaryBarAction extends Action2 { static readonly ID = 'workbench.action.focusAuxiliaryBar'; diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index e8fbbacb68a..b3be54f49d7 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -46,6 +46,9 @@ export class TogglePanelAction extends Action2 { icon: closeIcon, // Ensures no flickering when using toggled.icon f1: true, category: Categories.View, + metadata: { + description: localize('openAndClosePanel', 'Open/Show and Close/Hide Panel'), + }, keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyJ, weight: KeybindingWeight.WorkbenchContrib }, menu: [ { @@ -73,6 +76,21 @@ export class TogglePanelAction extends Action2 { registerAction2(TogglePanelAction); +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.closePanel', + title: localize2('closePanel', 'Hide Panel'), + category: Categories.View, + precondition: PanelVisibleContext, + f1: true, + }); + } + run(accessor: ServicesAccessor) { + accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.PANEL_PART); + } +}); + registerAction2(class extends Action2 { static readonly ID = 'workbench.action.focusPanel'; diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts b/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts index 408bb3e4eea..4256ecb5352 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarActions.ts @@ -13,6 +13,24 @@ import { KeybindingWeight } from '../../../../platform/keybinding/common/keybind import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js'; import { ViewContainerLocation } from '../../../common/views.js'; +import { SideBarVisibleContext } from '../../../common/contextkeys.js'; + +registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.action.closeSidebar', + title: localize2('closeSidebar', 'Close Primary Side Bar'), + category: Categories.View, + f1: true, + precondition: SideBarVisibleContext + }); + } + + run(accessor: ServicesAccessor): void { + accessor.get(IWorkbenchLayoutService).setPartHidden(true, Parts.SIDEBAR_PART); + } +}); export class FocusSideBarAction extends Action2 { From 224ade93d0510c71cbd362e7a8974b2e3df471d6 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:35:16 +0100 Subject: [PATCH 343/479] Git - don't use look-behind regex (#236447) --- extensions/git/src/git.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 4a968792a77..f2b9100d79a 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -502,7 +502,7 @@ export class Git { const repoUri = Uri.file(repositoryRootPath); const pathUri = Uri.file(pathInsidePossibleRepository); if (repoUri.authority.length !== 0 && pathUri.authority.length === 0) { - const match = /(?<=^\/?)([a-zA-Z])(?=:\/)/.exec(pathUri.path); + const match = /^[\/]?([a-zA-Z])[:\/]/.exec(pathUri.path); if (match !== null) { const [, letter] = match; From 29a39607d85dba549907a12b4ceece574d6dce51 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:40:30 +0100 Subject: [PATCH 344/479] Align panel badge style with activity bar (#236448) closes #195998 --- src/vs/workbench/browser/parts/panel/panelPart.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 6363f266384..d93cfd11b3d 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -16,7 +16,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { TogglePanelAction } from './panelActions.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND } from '../../../common/theme.js'; -import { badgeBackground, badgeForeground, contrastBorder } from '../../../../platform/theme/common/colorRegistry.js'; +import { contrastBorder } from '../../../../platform/theme/common/colorRegistry.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { Dimension } from '../../../../base/browser/dom.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; @@ -130,13 +130,12 @@ export class PanelPart extends AbstractPaneCompositePart { } protected getCompositeBarOptions(): IPaneCompositeBarOptions { - const showIcons = this.configurationService.getValue('workbench.panel.showLabels') === false; return { partContainerClass: 'panel', pinnedViewContainersKey: 'workbench.panel.pinnedPanels', placeholderViewContainersKey: 'workbench.panel.placeholderPanels', viewContainersWorkspaceStateKey: 'workbench.panel.viewContainersWorkspaceState', - icon: showIcons, + icon: this.configurationService.getValue('workbench.panel.showLabels') === false, orientation: ActionsOrientation.HORIZONTAL, recomputeSizes: true, activityHoverOptions: { @@ -153,8 +152,8 @@ export class PanelPart extends AbstractPaneCompositePart { activeBorderBottomColor: theme.getColor(PANEL_ACTIVE_TITLE_BORDER), activeForegroundColor: theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND), inactiveForegroundColor: theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND), - badgeBackground: theme.getColor(showIcons ? ACTIVITY_BAR_BADGE_BACKGROUND : badgeBackground), - badgeForeground: theme.getColor(showIcons ? ACTIVITY_BAR_BADGE_FOREGROUND : badgeForeground), + badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND), + badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND), dragAndDropBorder: theme.getColor(PANEL_DRAG_AND_DROP_BORDER) }) }; From 777fd07cccc3de449e529c9f701c2cfdd36ecb3e Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:20:00 +0100 Subject: [PATCH 345/479] Git - adopt #private in Git extension API (#236444) * Git - adopt #private in Git extension API * Fix post commit command provider --- extensions/git/src/api/api1.ts | 236 +++++++++++++---------- extensions/git/src/main.ts | 2 +- extensions/git/src/postCommitCommands.ts | 14 +- 3 files changed, 142 insertions(+), 110 deletions(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index a8a8fb694b1..8cc0b99f113 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions } from './git'; @@ -15,222 +17,242 @@ import { PickRemoteSourceOptions } from './git-base'; import { OperationKind, OperationResult } from '../operation'; class ApiInputBox implements InputBox { - set value(value: string) { this._inputBox.value = value; } - get value(): string { return this._inputBox.value; } - constructor(private _inputBox: SourceControlInputBox) { } + #inputBox: SourceControlInputBox; + + constructor(inputBox: SourceControlInputBox) { this.#inputBox = inputBox; } + + set value(value: string) { this.#inputBox.value = value; } + get value(): string { return this.#inputBox.value; } } export class ApiChange implements Change { + #resource: Resource; + constructor(resource: Resource) { this.#resource = resource; } - get uri(): Uri { return this.resource.resourceUri; } - get originalUri(): Uri { return this.resource.original; } - get renameUri(): Uri | undefined { return this.resource.renameResourceUri; } - get status(): Status { return this.resource.type; } - - constructor(private readonly resource: Resource) { } + get uri(): Uri { return this.#resource.resourceUri; } + get originalUri(): Uri { return this.#resource.original; } + get renameUri(): Uri | undefined { return this.#resource.renameResourceUri; } + get status(): Status { return this.#resource.type; } } export class ApiRepositoryState implements RepositoryState { + #repository: BaseRepository; + readonly onDidChange: Event; - get HEAD(): Branch | undefined { return this._repository.HEAD; } + constructor(repository: BaseRepository) { + this.#repository = repository; + this.onDidChange = this.#repository.onDidRunGitStatus; + } + + get HEAD(): Branch | undefined { return this.#repository.HEAD; } /** * @deprecated Use ApiRepository.getRefs() instead. */ get refs(): Ref[] { console.warn('Deprecated. Use ApiRepository.getRefs() instead.'); return []; } - get remotes(): Remote[] { return [...this._repository.remotes]; } - get submodules(): Submodule[] { return [...this._repository.submodules]; } - get rebaseCommit(): Commit | undefined { return this._repository.rebaseCommit; } - - get mergeChanges(): Change[] { return this._repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); } - get indexChanges(): Change[] { return this._repository.indexGroup.resourceStates.map(r => new ApiChange(r)); } - get workingTreeChanges(): Change[] { return this._repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); } - get untrackedChanges(): Change[] { return this._repository.untrackedGroup.resourceStates.map(r => new ApiChange(r)); } - - readonly onDidChange: Event = this._repository.onDidRunGitStatus; - - constructor(private _repository: BaseRepository) { } + get remotes(): Remote[] { return [...this.#repository.remotes]; } + get submodules(): Submodule[] { return [...this.#repository.submodules]; } + get rebaseCommit(): Commit | undefined { return this.#repository.rebaseCommit; } + + get mergeChanges(): Change[] { return this.#repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); } + get indexChanges(): Change[] { return this.#repository.indexGroup.resourceStates.map(r => new ApiChange(r)); } + get workingTreeChanges(): Change[] { return this.#repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); } + get untrackedChanges(): Change[] { return this.#repository.untrackedGroup.resourceStates.map(r => new ApiChange(r)); } } export class ApiRepositoryUIState implements RepositoryUIState { + #sourceControl: SourceControl; + readonly onDidChange: Event; - get selected(): boolean { return this._sourceControl.selected; } - - readonly onDidChange: Event = mapEvent(this._sourceControl.onDidChangeSelection, () => null); + constructor(sourceControl: SourceControl) { + this.#sourceControl = sourceControl; + this.onDidChange = mapEvent(this.#sourceControl.onDidChangeSelection, () => null); + } - constructor(private _sourceControl: SourceControl) { } + get selected(): boolean { return this.#sourceControl.selected; } } export class ApiRepository implements Repository { - readonly rootUri: Uri = Uri.file(this.repository.root); - readonly inputBox: InputBox = new ApiInputBox(this.repository.inputBox); - readonly state: RepositoryState = new ApiRepositoryState(this.repository); - readonly ui: RepositoryUIState = new ApiRepositoryUIState(this.repository.sourceControl); + #repository: BaseRepository; + + readonly rootUri: Uri; + readonly inputBox: InputBox; + readonly state: RepositoryState; + readonly ui: RepositoryUIState; - readonly onDidCommit: Event = mapEvent( - filterEvent(this.repository.onDidRunOperation, e => e.operation.kind === OperationKind.Commit), () => null); + readonly onDidCommit: Event; + readonly onDidCheckout: Event; - readonly onDidCheckout: Event = mapEvent( - filterEvent(this.repository.onDidRunOperation, e => e.operation.kind === OperationKind.Checkout || e.operation.kind === OperationKind.CheckoutTracking), () => null); + constructor(repository: BaseRepository) { + this.#repository = repository; - constructor(readonly repository: BaseRepository) { } + this.rootUri = Uri.file(this.#repository.root); + this.inputBox = new ApiInputBox(this.#repository.inputBox); + this.state = new ApiRepositoryState(this.#repository); + this.ui = new ApiRepositoryUIState(this.#repository.sourceControl); + + this.onDidCommit = mapEvent( + filterEvent(this.#repository.onDidRunOperation, e => e.operation.kind === OperationKind.Commit), () => null); + this.onDidCheckout = mapEvent( + filterEvent(this.#repository.onDidRunOperation, e => e.operation.kind === OperationKind.Checkout || e.operation.kind === OperationKind.CheckoutTracking), () => null); + } apply(patch: string, reverse?: boolean): Promise { - return this.repository.apply(patch, reverse); + return this.#repository.apply(patch, reverse); } getConfigs(): Promise<{ key: string; value: string }[]> { - return this.repository.getConfigs(); + return this.#repository.getConfigs(); } getConfig(key: string): Promise { - return this.repository.getConfig(key); + return this.#repository.getConfig(key); } setConfig(key: string, value: string): Promise { - return this.repository.setConfig(key, value); + return this.#repository.setConfig(key, value); } getGlobalConfig(key: string): Promise { - return this.repository.getGlobalConfig(key); + return this.#repository.getGlobalConfig(key); } getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }> { - return this.repository.getObjectDetails(treeish, path); + return this.#repository.getObjectDetails(treeish, path); } detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { - return this.repository.detectObjectType(object); + return this.#repository.detectObjectType(object); } buffer(ref: string, filePath: string): Promise { - return this.repository.buffer(ref, filePath); + return this.#repository.buffer(ref, filePath); } show(ref: string, path: string): Promise { - return this.repository.show(ref, path); + return this.#repository.show(ref, path); } getCommit(ref: string): Promise { - return this.repository.getCommit(ref); + return this.#repository.getCommit(ref); } add(paths: string[]) { - return this.repository.add(paths.map(p => Uri.file(p))); + return this.#repository.add(paths.map(p => Uri.file(p))); } revert(paths: string[]) { - return this.repository.revert(paths.map(p => Uri.file(p))); + return this.#repository.revert(paths.map(p => Uri.file(p))); } clean(paths: string[]) { - return this.repository.clean(paths.map(p => Uri.file(p))); + return this.#repository.clean(paths.map(p => Uri.file(p))); } diff(cached?: boolean) { - return this.repository.diff(cached); + return this.#repository.diff(cached); } diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; diffWithHEAD(path?: string): Promise { - return this.repository.diffWithHEAD(path); + return this.#repository.diffWithHEAD(path); } diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; diffWith(ref: string, path?: string): Promise { - return this.repository.diffWith(ref, path); + return this.#repository.diffWith(ref, path); } diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; diffIndexWithHEAD(path?: string): Promise { - return this.repository.diffIndexWithHEAD(path); + return this.#repository.diffIndexWithHEAD(path); } diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffIndexWith(ref: string, path?: string): Promise { - return this.repository.diffIndexWith(ref, path); + return this.#repository.diffIndexWith(ref, path); } diffBlobs(object1: string, object2: string): Promise { - return this.repository.diffBlobs(object1, object2); + return this.#repository.diffBlobs(object1, object2); } diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; diffBetween(ref1: string, ref2: string, path?: string): Promise { - return this.repository.diffBetween(ref1, ref2, path); + return this.#repository.diffBetween(ref1, ref2, path); } hashObject(data: string): Promise { - return this.repository.hashObject(data); + return this.#repository.hashObject(data); } createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise { - return this.repository.branch(name, checkout, ref); + return this.#repository.branch(name, checkout, ref); } deleteBranch(name: string, force?: boolean): Promise { - return this.repository.deleteBranch(name, force); + return this.#repository.deleteBranch(name, force); } getBranch(name: string): Promise { - return this.repository.getBranch(name); + return this.#repository.getBranch(name); } getBranches(query: BranchQuery, cancellationToken?: CancellationToken): Promise { - return this.repository.getBranches(query, cancellationToken); + return this.#repository.getBranches(query, cancellationToken); } getBranchBase(name: string): Promise { - return this.repository.getBranchBase(name); + return this.#repository.getBranchBase(name); } setBranchUpstream(name: string, upstream: string): Promise { - return this.repository.setBranchUpstream(name, upstream); + return this.#repository.setBranchUpstream(name, upstream); } getRefs(query: RefQuery, cancellationToken?: CancellationToken): Promise { - return this.repository.getRefs(query, cancellationToken); + return this.#repository.getRefs(query, cancellationToken); } checkIgnore(paths: string[]): Promise> { - return this.repository.checkIgnore(paths); + return this.#repository.checkIgnore(paths); } getMergeBase(ref1: string, ref2: string): Promise { - return this.repository.getMergeBase(ref1, ref2); + return this.#repository.getMergeBase(ref1, ref2); } tag(name: string, message: string, ref?: string | undefined): Promise { - return this.repository.tag({ name, message, ref }); + return this.#repository.tag({ name, message, ref }); } deleteTag(name: string): Promise { - return this.repository.deleteTag(name); + return this.#repository.deleteTag(name); } status(): Promise { - return this.repository.status(); + return this.#repository.status(); } checkout(treeish: string): Promise { - return this.repository.checkout(treeish); + return this.#repository.checkout(treeish); } addRemote(name: string, url: string): Promise { - return this.repository.addRemote(name, url); + return this.#repository.addRemote(name, url); } removeRemote(name: string): Promise { - return this.repository.removeRemote(name); + return this.#repository.removeRemote(name); } renameRemote(name: string, newName: string): Promise { - return this.repository.renameRemote(name, newName); + return this.#repository.renameRemote(name, newName); } fetch(arg0?: FetchOptions | string | undefined, @@ -239,86 +261,92 @@ export class ApiRepository implements Repository { prune?: boolean | undefined ): Promise { if (arg0 !== undefined && typeof arg0 !== 'string') { - return this.repository.fetch(arg0); + return this.#repository.fetch(arg0); } - return this.repository.fetch({ remote: arg0, ref, depth, prune }); + return this.#repository.fetch({ remote: arg0, ref, depth, prune }); } pull(unshallow?: boolean): Promise { - return this.repository.pull(undefined, unshallow); + return this.#repository.pull(undefined, unshallow); } push(remoteName?: string, branchName?: string, setUpstream: boolean = false, force?: ForcePushMode): Promise { - return this.repository.pushTo(remoteName, branchName, setUpstream, force); + return this.#repository.pushTo(remoteName, branchName, setUpstream, force); } blame(path: string): Promise { - return this.repository.blame(path); + return this.#repository.blame(path); } log(options?: LogOptions): Promise { - return this.repository.log(options); + return this.#repository.log(options); } commit(message: string, opts?: CommitOptions): Promise { - return this.repository.commit(message, { ...opts, postCommitCommand: null }); + return this.#repository.commit(message, { ...opts, postCommitCommand: null }); } merge(ref: string): Promise { - return this.repository.merge(ref); + return this.#repository.merge(ref); } mergeAbort(): Promise { - return this.repository.mergeAbort(); + return this.#repository.mergeAbort(); } applyStash(index?: number): Promise { - return this.repository.applyStash(index); + return this.#repository.applyStash(index); } popStash(index?: number): Promise { - return this.repository.popStash(index); + return this.#repository.popStash(index); } dropStash(index?: number): Promise { - return this.repository.dropStash(index); + return this.#repository.dropStash(index); } } export class ApiGit implements Git { + #model: Model; - get path(): string { return this._model.git.path; } + constructor(model: Model) { this.#model = model; } - constructor(private _model: Model) { } + get path(): string { return this.#model.git.path; } } export class ApiImpl implements API { + #model: Model; + readonly git: ApiGit; - readonly git = new ApiGit(this._model); + constructor(model: Model) { + this.#model = model; + this.git = new ApiGit(this.#model); + } get state(): APIState { - return this._model.state; + return this.#model.state; } get onDidChangeState(): Event { - return this._model.onDidChangeState; + return this.#model.onDidChangeState; } get onDidPublish(): Event { - return this._model.onDidPublish; + return this.#model.onDidPublish; } get onDidOpenRepository(): Event { - return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r)); + return mapEvent(this.#model.onDidOpenRepository, r => new ApiRepository(r)); } get onDidCloseRepository(): Event { - return mapEvent(this._model.onDidCloseRepository, r => new ApiRepository(r)); + return mapEvent(this.#model.onDidCloseRepository, r => new ApiRepository(r)); } get repositories(): Repository[] { - return this._model.repositories.map(r => new ApiRepository(r)); + return this.#model.repositories.map(r => new ApiRepository(r)); } toGitUri(uri: Uri, ref: string): Uri { @@ -326,14 +354,14 @@ export class ApiImpl implements API { } getRepository(uri: Uri): Repository | null { - const result = this._model.getRepository(uri); + const result = this.#model.getRepository(uri); return result ? new ApiRepository(result) : null; } async init(root: Uri, options?: InitOptions): Promise { const path = root.fsPath; - await this._model.git.init(path, options); - await this._model.openRepository(path); + await this.#model.git.init(path, options); + await this.#model.openRepository(path); return this.getRepository(root) || null; } @@ -342,7 +370,7 @@ export class ApiImpl implements API { return null; } - await this._model.openRepository(root.fsPath); + await this.#model.openRepository(root.fsPath); return this.getRepository(root) || null; } @@ -350,7 +378,7 @@ export class ApiImpl implements API { const disposables: Disposable[] = []; if (provider.publishRepository) { - disposables.push(this._model.registerRemoteSourcePublisher(provider as RemoteSourcePublisher)); + disposables.push(this.#model.registerRemoteSourcePublisher(provider as RemoteSourcePublisher)); } disposables.push(GitBaseApi.getAPI().registerRemoteSourceProvider(provider)); @@ -358,26 +386,24 @@ export class ApiImpl implements API { } registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable { - return this._model.registerRemoteSourcePublisher(publisher); + return this.#model.registerRemoteSourcePublisher(publisher); } registerCredentialsProvider(provider: CredentialsProvider): Disposable { - return this._model.registerCredentialsProvider(provider); + return this.#model.registerCredentialsProvider(provider); } registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable { - return this._model.registerPostCommitCommandsProvider(provider); + return this.#model.registerPostCommitCommandsProvider(provider); } registerPushErrorHandler(handler: PushErrorHandler): Disposable { - return this._model.registerPushErrorHandler(handler); + return this.#model.registerPushErrorHandler(handler); } registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable { - return this._model.registerBranchProtectionProvider(root, provider); + return this.#model.registerBranchProtectionProvider(root, provider); } - - constructor(private _model: Model) { } } function getRefType(type: RefType): string { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 7180890ad84..515f57c12cf 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -121,7 +121,7 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, new TerminalShellExecutionManager(model, logger) ); - const postCommitCommandsProvider = new GitPostCommitCommandsProvider(); + const postCommitCommandsProvider = new GitPostCommitCommandsProvider(model); model.registerPostCommitCommandsProvider(postCommitCommandsProvider); const diagnosticsManager = new GitCommitInputBoxDiagnosticsManager(model); diff --git a/extensions/git/src/postCommitCommands.ts b/extensions/git/src/postCommitCommands.ts index d4e227b6db7..69a18114a41 100644 --- a/extensions/git/src/postCommitCommands.ts +++ b/extensions/git/src/postCommitCommands.ts @@ -5,7 +5,7 @@ import { Command, commands, Disposable, Event, EventEmitter, Memento, Uri, workspace, l10n } from 'vscode'; import { PostCommitCommandsProvider } from './api/git'; -import { Repository } from './repository'; +import { IRepositoryResolver, Repository } from './repository'; import { ApiRepository } from './api/api1'; import { dispose } from './util'; import { OperationKind } from './operation'; @@ -18,17 +18,23 @@ export interface IPostCommitCommandsProviderRegistry { } export class GitPostCommitCommandsProvider implements PostCommitCommandsProvider { + constructor(private readonly _repositoryResolver: IRepositoryResolver) { } + getCommands(apiRepository: ApiRepository): Command[] { - const config = workspace.getConfiguration('git', Uri.file(apiRepository.repository.root)); + const repository = this._repositoryResolver.getRepository(apiRepository.rootUri); + if (!repository) { + return []; + } + + const config = workspace.getConfiguration('git', Uri.file(repository.root)); // Branch protection - const isBranchProtected = apiRepository.repository.isBranchProtected(); + const isBranchProtected = repository.isBranchProtected(); const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!; const alwaysPrompt = isBranchProtected && branchProtectionPrompt === 'alwaysPrompt'; const alwaysCommitToNewBranch = isBranchProtected && branchProtectionPrompt === 'alwaysCommitToNewBranch'; // Icon - const repository = apiRepository.repository; const isCommitInProgress = repository.operations.isRunning(OperationKind.Commit) || repository.operations.isRunning(OperationKind.PostCommitCommand); const icon = isCommitInProgress ? '$(sync~spin)' : alwaysPrompt ? '$(lock)' : alwaysCommitToNewBranch ? '$(git-branch)' : undefined; From 9fb1d574701dd41b6e5014e1c20f3cc7c4e4f353 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 18 Dec 2024 11:34:24 +0100 Subject: [PATCH 346/479] Reapply "debt: clean up obsolete file usage" (#236453) * Reapply "debt: clean up obsolete file usage" (#236433) This reverts commit cf2ebd91b8e42e3bc5ab0e85e3323c886a977ffe. * Fix extension deletion logic to ensure proper removal and handle file not found errors --- .../common/extensionManagement.ts | 4 +- .../common/extensionsScannerService.ts | 51 ++---- .../node/extensionManagementService.ts | 171 +++++++++--------- .../node/extensionsWatcher.ts | 20 +- .../node/extensionsScannerService.test.ts | 33 +--- 5 files changed, 122 insertions(+), 157 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 155079831fc..f08b46ae65b 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -456,8 +456,8 @@ export const enum ExtensionManagementErrorCode { Extract = 'Extract', Scanning = 'Scanning', ScanningExtension = 'ScanningExtension', - ReadUninstalled = 'ReadUninstalled', - UnsetUninstalled = 'UnsetUninstalled', + ReadRemoved = 'ReadRemoved', + UnsetRemoved = 'UnsetRemoved', Delete = 'Delete', Rename = 'Rename', IntializeDefaultProfile = 'IntializeDefaultProfile', diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 99868cddb5d..a186b6fa045 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -7,7 +7,6 @@ import { coalesce } from '../../../base/common/arrays.js'; import { ThrottledDelayer } from '../../../base/common/async.js'; import * as objects from '../../../base/common/objects.js'; import { VSBuffer } from '../../../base/common/buffer.js'; -import { IStringDictionary } from '../../../base/common/collections.js'; import { getErrorMessage } from '../../../base/common/errors.js'; import { getNodeType, parse, ParseError } from '../../../base/common/json.js'; import { getParseErrorMessage } from '../../../base/common/jsonErrorMessages.js'; @@ -18,12 +17,11 @@ import * as platform from '../../../base/common/platform.js'; import { basename, isEqual, joinPath } from '../../../base/common/resources.js'; import * as semver from '../../../base/common/semver/semver.js'; import Severity from '../../../base/common/severity.js'; -import { isEmptyObject } from '../../../base/common/types.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { IEnvironmentService } from '../../environment/common/environment.js'; import { IProductVersion, Metadata } from './extensionManagement.js'; -import { areSameExtensions, computeTargetPlatform, ExtensionKey, getExtensionId, getGalleryExtensionId } from './extensionManagementUtil.js'; +import { areSameExtensions, computeTargetPlatform, getExtensionId, getGalleryExtensionId } from './extensionManagementUtil.js'; import { ExtensionType, ExtensionIdentifier, IExtensionManifest, TargetPlatform, IExtensionIdentifier, IRelaxedExtensionManifest, UNDEFINED_PUBLISHER, IExtensionDescription, BUILTIN_MANIFEST_CACHE_FILE, USER_MANIFEST_CACHE_FILE, ExtensionIdentifierMap, parseEnabledApiProposalNames } from '../../extensions/common/extensions.js'; import { validateExtensionManifest } from '../../extensions/common/extensionValidator.js'; import { FileOperationResult, IFileService, toFileOperationResult } from '../../files/common/files.js'; @@ -106,7 +104,6 @@ export type ScanOptions = { readonly profileLocation?: URI; readonly includeInvalid?: boolean; readonly includeAllVersions?: boolean; - readonly includeUninstalled?: boolean; readonly checkControlFile?: boolean; readonly language?: string; readonly useCache?: boolean; @@ -145,10 +142,9 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private readonly _onDidChangeCache = this._register(new Emitter()); readonly onDidChangeCache = this._onDidChangeCache.event; - private readonly obsoleteFile = joinPath(this.userExtensionsLocation, '.obsolete'); - private readonly systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile)); - private readonly userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile, this.obsoleteFile)); - private readonly extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner, this.obsoleteFile)); + private readonly systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile)); + private readonly userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, this.currentProfile)); + private readonly extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner)); constructor( readonly systemExtensionsLocation: URI, @@ -199,8 +195,8 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem const location = scanOptions.profileLocation ?? this.userExtensionsLocation; this.logService.trace('Started scanning user extensions', location); const profileScanOptions: IProfileExtensionsScanOptions | undefined = this.uriIdentityService.extUri.isEqual(scanOptions.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource) ? { bailOutWhenFileNotFound: true } : undefined; - const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion()); - const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner; + const extensionsScannerInput = await this.createExtensionScannerInput(location, !!scanOptions.profileLocation, ExtensionType.User, scanOptions.language, true, profileScanOptions, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode ? this.userExtensionsCachedScanner : this.extensionsScanner; let extensions: IRelaxedScannedExtension[]; try { extensions = await extensionsScanner.scanExtensions(extensionsScannerInput); @@ -221,7 +217,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionDevelopmentLocationURI) { const extensions = (await Promise.all(this.environmentService.extensionDevelopmentLocationURI.filter(extLoc => extLoc.scheme === Schemas.file) .map(async extensionDevelopmentLocationURI => { - const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, true, scanOptions.language, false /* do not validate */, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const input = await this.createExtensionScannerInput(extensionDevelopmentLocationURI, false, ExtensionType.User, scanOptions.language, false /* do not validate */, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(input); return extensions.map(extension => { // Override the extension type from the existing extensions @@ -237,7 +233,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extension = await this.extensionsScanner.scanExtension(extensionsScannerInput); if (!extension) { return null; @@ -249,7 +245,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } async scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { - const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, scanOptions.language, true, undefined, scanOptions.productVersion ?? this.getProductVersion()); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput); return this.applyScanOptions(extensions, extensionType, scanOptions, true); } @@ -405,7 +401,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem private async scanDefaultSystemExtensions(useCache: boolean, language: string | undefined): Promise { this.logService.trace('Started scanning system extensions'); - const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion()); + const extensionsScannerInput = await this.createExtensionScannerInput(this.systemExtensionsLocation, false, ExtensionType.System, language, true, undefined, this.getProductVersion()); const extensionsScanner = useCache && !extensionsScannerInput.devMode ? this.systemExtensionsCachedScanner : this.extensionsScanner; const result = await extensionsScanner.scanExtensions(extensionsScannerInput); this.logService.trace('Scanned system extensions:', result.length); @@ -435,7 +431,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem break; } } - const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, true, language, true, undefined, this.getProductVersion()))))); + const result = await Promise.all(devSystemExtensionsLocations.map(async location => this.extensionsScanner.scanExtension((await this.createExtensionScannerInput(location, false, ExtensionType.System, language, true, undefined, this.getProductVersion()))))); this.logService.trace('Scanned dev system extensions:', result.length); return coalesce(result); } @@ -449,7 +445,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem } } - private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, excludeObsolete: boolean, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise { + private async createExtensionScannerInput(location: URI, profile: boolean, type: ExtensionType, language: string | undefined, validate: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined, productVersion: IProductVersion): Promise { const translations = await this.getTranslations(language ?? platform.language); const mtime = await this.getMtime(location); const applicationExtensionsLocation = profile && !this.uriIdentityService.extUri.isEqual(location, this.userDataProfilesService.defaultProfile.extensionsResource) ? this.userDataProfilesService.defaultProfile.extensionsResource : undefined; @@ -462,7 +458,6 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem profile, profileScanOptions, type, - excludeObsolete, validate, productVersion.version, productVersion.date, @@ -504,7 +499,6 @@ export class ExtensionScannerInput { public readonly profile: boolean, public readonly profileScanOptions: IProfileExtensionsScanOptions | undefined, public readonly type: ExtensionType, - public readonly excludeObsolete: boolean, public readonly validate: boolean, public readonly productVersion: string, public readonly productDate: string | undefined, @@ -534,7 +528,6 @@ export class ExtensionScannerInput { && a.profile === b.profile && objects.equals(a.profileScanOptions, b.profileScanOptions) && a.type === b.type - && a.excludeObsolete === b.excludeObsolete && a.validate === b.validate && a.productVersion === b.productVersion && a.productDate === b.productDate @@ -558,7 +551,6 @@ class ExtensionsScanner extends Disposable { private readonly extensionsEnabledWithApiProposalVersion: string[]; constructor( - private readonly obsoleteFile: URI, @IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService, @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService, @IFileService protected readonly fileService: IFileService, @@ -571,15 +563,9 @@ class ExtensionsScanner extends Disposable { } async scanExtensions(input: ExtensionScannerInput): Promise { - const extensions = input.profile ? await this.scanExtensionsFromProfile(input) : await this.scanExtensionsFromLocation(input); - let obsolete: IStringDictionary = {}; - if (input.excludeObsolete && input.type === ExtensionType.User) { - try { - const raw = (await this.fileService.readFile(this.obsoleteFile)).value.toString(); - obsolete = JSON.parse(raw); - } catch (error) { /* ignore */ } - } - return isEmptyObject(obsolete) ? extensions : extensions.filter(e => !obsolete[ExtensionKey.create(e).toString()]); + return input.profile + ? this.scanExtensionsFromProfile(input) + : this.scanExtensionsFromLocation(input); } private async scanExtensionsFromLocation(input: ExtensionScannerInput): Promise { @@ -596,7 +582,7 @@ class ExtensionsScanner extends Disposable { if (input.type === ExtensionType.User && basename(c.resource).indexOf('.') === 0) { return null; } - const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + const extensionScannerInput = new ExtensionScannerInput(c.resource, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); return this.scanExtension(extensionScannerInput); })); return coalesce(extensions) @@ -622,7 +608,7 @@ class ExtensionsScanner extends Disposable { const extensions = await Promise.all( scannedProfileExtensions.map(async extensionInfo => { if (filter(extensionInfo)) { - const extensionScannerInput = new ExtensionScannerInput(extensionInfo.location, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.excludeObsolete, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); + const extensionScannerInput = new ExtensionScannerInput(extensionInfo.location, input.mtime, input.applicationExtensionslocation, input.applicationExtensionslocationMtime, input.profile, input.profileScanOptions, input.type, input.validate, input.productVersion, input.productDate, input.productCommit, input.devMode, input.language, input.translations); return this.scanExtension(extensionScannerInput, extensionInfo.metadata); } return null; @@ -891,7 +877,6 @@ class CachedExtensionsScanner extends ExtensionsScanner { constructor( private readonly currentProfile: IUserDataProfile, - obsoleteFile: URI, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService, @IUriIdentityService uriIdentityService: IUriIdentityService, @@ -900,7 +885,7 @@ class CachedExtensionsScanner extends ExtensionsScanner { @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService ) { - super(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService); + super(extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService); } override async scanExtensions(input: ExtensionScannerInput): Promise { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 92405eefb75..762da10967f 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -60,7 +60,7 @@ export interface INativeServerExtensionManagementService extends IExtensionManag readonly _serviceBrand: undefined; scanAllUserInstalledExtensions(): Promise; scanInstalledExtensionAtLocation(location: URI): Promise; - markAsUninstalled(...extensions: IExtension[]): Promise; + deleteExtensions(...extensions: IExtension[]): Promise; } type ExtractExtensionResult = { readonly local: ILocalExtension; readonly verificationStatus?: ExtensionSignatureVerificationCode }; @@ -222,8 +222,8 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return this.extensionsScanner.copyExtensions(fromProfileLocation, toProfileLocation, { version: this.productService.version, date: this.productService.date }); } - markAsUninstalled(...extensions: IExtension[]): Promise { - return this.extensionsScanner.setUninstalled(...extensions); + deleteExtensions(...extensions: IExtension[]): Promise { + return this.extensionsScanner.setExtensionsForRemoval(...extensions); } async cleanUp(): Promise { @@ -480,8 +480,20 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi continue; } - // Check if this is a directory - if (!(await this.fileService.stat(resource)).isDirectory) { + // Ignore changes to the deleted folder + if (this.uriIdentityService.extUri.basename(resource).endsWith(DELETED_FOLDER_POSTFIX)) { + continue; + } + + try { + // Check if this is a directory + if (!(await this.fileService.stat(resource)).isDirectory) { + continue; + } + } catch (error) { + if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { + this.logService.error(error); + } continue; } @@ -502,23 +514,10 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi private async addExtensionsToProfile(extensions: [ILocalExtension, Metadata | undefined][], profileLocation: URI): Promise { const localExtensions = extensions.map(e => e[0]); - await this.setInstalled(localExtensions); + await this.extensionsScanner.unsetExtensionsForRemoval(...localExtensions.map(extension => ExtensionKey.create(extension))); await this.extensionsProfileScannerService.addExtensionsToProfile(extensions, profileLocation); this._onDidInstallExtensions.fire(localExtensions.map(local => ({ local, identifier: local.identifier, operation: InstallOperation.None, profileLocation }))); } - - private async setInstalled(extensions: ILocalExtension[]): Promise { - const uninstalled = await this.extensionsScanner.getUninstalledExtensions(); - for (const extension of extensions) { - const extensionKey = ExtensionKey.create(extension); - if (!uninstalled[extensionKey.toString()]) { - continue; - } - this.logService.trace('Removing the extension from uninstalled list:', extensionKey.id); - await this.extensionsScanner.setInstalled(extensionKey); - this.logService.info('Removed the extension from uninstalled list:', extensionKey.id); - } - } } type UpdateMetadataErrorClassification = { @@ -536,8 +535,8 @@ type UpdateMetadataErrorEvent = { export class ExtensionsScanner extends Disposable { - private readonly uninstalledResource: URI; - private readonly uninstalledFileLimiter: Queue; + private readonly obsoletedResource: URI; + private readonly obsoleteFileLimiter: Queue; private readonly _onExtract = this._register(new Emitter()); readonly onExtract = this._onExtract.event; @@ -555,13 +554,13 @@ export class ExtensionsScanner extends Disposable { @ILogService private readonly logService: ILogService, ) { super(); - this.uninstalledResource = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete'); - this.uninstalledFileLimiter = new Queue(); + this.obsoletedResource = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete'); + this.obsoleteFileLimiter = new Queue(); } async cleanUp(): Promise { await this.removeTemporarilyDeletedFolders(); - await this.removeUninstalledExtensions(); + await this.deleteExtensionsMarkedForRemoval(); await this.initializeMetadata(); } @@ -720,42 +719,40 @@ export class ExtensionsScanner extends Disposable { return this.scanLocalExtension(local.location, local.type, profileLocation); } - async getUninstalledExtensions(): Promise> { - try { - return await this.withUninstalledExtensions(); - } catch (error) { - throw toExtensionManagementError(error, ExtensionManagementErrorCode.ReadUninstalled); - } - } - - async setUninstalled(...extensions: IExtension[]): Promise { + async setExtensionsForRemoval(...extensions: IExtension[]): Promise { const extensionKeys: ExtensionKey[] = extensions.map(e => ExtensionKey.create(e)); - await this.withUninstalledExtensions(uninstalled => + await this.withRemovedExtensions(removedExtensions => extensionKeys.forEach(extensionKey => { - uninstalled[extensionKey.toString()] = true; - this.logService.info('Marked extension as uninstalled', extensionKey.toString()); + removedExtensions[extensionKey.toString()] = true; + this.logService.info('Marked extension as removed', extensionKey.toString()); })); } - async setInstalled(extensionKey: ExtensionKey): Promise { + async unsetExtensionsForRemoval(...extensionKeys: ExtensionKey[]): Promise { try { - await this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionKey.toString()]); + const results: boolean[] = []; + await this.withRemovedExtensions(removedExtensions => + extensionKeys.forEach(extensionKey => { + if (removedExtensions[extensionKey.toString()]) { + results.push(true); + delete removedExtensions[extensionKey.toString()]; + } else { + results.push(false); + } + })); + return results; } catch (error) { - throw toExtensionManagementError(error, ExtensionManagementErrorCode.UnsetUninstalled); + throw toExtensionManagementError(error, ExtensionManagementErrorCode.UnsetRemoved); } } - async removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { + async deleteExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { if (this.uriIdentityService.extUri.isEqualOrParent(extension.location, this.extensionsScannerService.userExtensionsLocation)) { - return this.deleteExtensionFromLocation(extension.identifier.id, extension.location, type); + await this.deleteExtensionFromLocation(extension.identifier.id, extension.location, type); + await this.unsetExtensionsForRemoval(ExtensionKey.create(extension)); } } - async removeUninstalledExtension(extension: ILocalExtension | IScannedExtension): Promise { - await this.removeExtension(extension, 'uninstalled'); - await this.withUninstalledExtensions(uninstalled => delete uninstalled[ExtensionKey.create(extension).toString()]); - } - async copyExtension(extension: ILocalExtension, fromProfileLocation: URI, toProfileLocation: URI, metadata: Partial): Promise { const source = await this.getScannedExtension(extension, fromProfileLocation); const target = await this.getScannedExtension(extension, toProfileLocation); @@ -792,11 +789,11 @@ export class ExtensionsScanner extends Disposable { this.logService.info(`Deleted ${type} extension from disk`, id, location.fsPath); } - private withUninstalledExtensions(updateFn?: (uninstalled: IStringDictionary) => void): Promise> { - return this.uninstalledFileLimiter.queue(async () => { + private withRemovedExtensions(updateFn?: (removed: IStringDictionary) => void): Promise> { + return this.obsoleteFileLimiter.queue(async () => { let raw: string | undefined; try { - const content = await this.fileService.readFile(this.uninstalledResource, 'utf8'); + const content = await this.fileService.readFile(this.obsoletedResource, 'utf8'); raw = content.value.toString(); } catch (error) { if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { @@ -804,23 +801,29 @@ export class ExtensionsScanner extends Disposable { } } - let uninstalled = {}; + let removed = {}; if (raw) { try { - uninstalled = JSON.parse(raw); + removed = JSON.parse(raw); } catch (e) { /* ignore */ } } if (updateFn) { - updateFn(uninstalled); - if (Object.keys(uninstalled).length) { - await this.fileService.writeFile(this.uninstalledResource, VSBuffer.fromString(JSON.stringify(uninstalled))); + updateFn(removed); + if (Object.keys(removed).length) { + await this.fileService.writeFile(this.obsoletedResource, VSBuffer.fromString(JSON.stringify(removed))); } else { - await this.fileService.del(this.uninstalledResource); + try { + await this.fileService.del(this.obsoletedResource); + } catch (error) { + if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) { + throw error; + } + } } } - return uninstalled; + return removed; }); } @@ -898,19 +901,25 @@ export class ExtensionsScanner extends Disposable { })); } - private async removeUninstalledExtensions(): Promise { - const uninstalled = await this.getUninstalledExtensions(); - if (Object.keys(uninstalled).length === 0) { - this.logService.debug(`No uninstalled extensions found.`); + private async deleteExtensionsMarkedForRemoval(): Promise { + let removed: IStringDictionary; + try { + removed = await this.withRemovedExtensions(); + } catch (error) { + throw toExtensionManagementError(error, ExtensionManagementErrorCode.ReadRemoved); + } + + if (Object.keys(removed).length === 0) { + this.logService.debug(`No extensions are marked as removed.`); return; } - this.logService.debug(`Removing uninstalled extensions:`, Object.keys(uninstalled)); + this.logService.debug(`Deleting extensions marked as removed:`, Object.keys(removed)); - const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeUninstalled: true, includeInvalid: true }); // All user extensions + const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeInvalid: true }); // All user extensions const installed: Set = new Set(); for (const e of extensions) { - if (!uninstalled[ExtensionKey.create(e).toString()]) { + if (!removed[ExtensionKey.create(e).toString()]) { installed.add(e.identifier.id.toLowerCase()); } } @@ -928,8 +937,8 @@ export class ExtensionsScanner extends Disposable { this.logService.error(error); } - const toRemove = extensions.filter(e => e.metadata /* Installed by System */ && uninstalled[ExtensionKey.create(e).toString()]); - await Promise.allSettled(toRemove.map(e => this.removeUninstalledExtension(e))); + const toRemove = extensions.filter(e => e.metadata /* Installed by System */ && removed[ExtensionKey.create(e).toString()]); + await Promise.allSettled(toRemove.map(e => this.deleteExtension(e, 'marked for removal'))); } private async removeTemporarilyDeletedFolders(): Promise { @@ -1021,7 +1030,7 @@ class InstallExtensionInProfileTask extends AbstractExtensionTask { - const uninstalled = await this.extensionsScanner.getUninstalledExtensions(); - if (!uninstalled[extensionKey.toString()]) { - return undefined; + private async unsetIfRemoved(extensionKey: ExtensionKey): Promise { + // If the same version of extension is marked as removed, remove it from there and return the local. + const [removed] = await this.extensionsScanner.unsetExtensionsForRemoval(extensionKey); + if (removed) { + this.logService.info('Removed the extension from removed list:', extensionKey.id); + const userExtensions = await this.extensionsScanner.scanAllUserExtensions(true); + return userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)); } - - this.logService.trace('Removing the extension from uninstalled list:', extensionKey.id); - // If the same version of extension is marked as uninstalled, remove it from there and return the local. - await this.extensionsScanner.setInstalled(extensionKey); - this.logService.info('Removed the extension from uninstalled list:', extensionKey.id); - - const userExtensions = await this.extensionsScanner.scanAllUserExtensions(true); - return userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)); + return undefined; } private async updateMetadata(extension: ILocalExtension, token: CancellationToken): Promise { @@ -1149,8 +1154,8 @@ class UninstallExtensionInProfileTask extends AbstractExtensionTask implem super(); } - protected async doRun(token: CancellationToken): Promise { - await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension, this.options.profileLocation); + protected doRun(token: CancellationToken): Promise { + return this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension, this.options.profileLocation); } } diff --git a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts index d2b65eaea55..2c4e976a5a6 100644 --- a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts +++ b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts @@ -48,7 +48,7 @@ export class ExtensionsWatcher extends Disposable { await this.extensionsScannerService.initializeDefaultProfileExtensions(); await this.onDidChangeProfiles(this.userDataProfilesService.profiles); this.registerListeners(); - await this.uninstallExtensionsNotInProfiles(); + await this.deleteExtensionsNotInProfiles(); } private registerListeners(): void { @@ -102,7 +102,7 @@ export class ExtensionsWatcher extends Disposable { } private async onDidRemoveExtensions(e: DidRemoveProfileExtensionsEvent): Promise { - const extensionsToUninstall: IExtension[] = []; + const extensionsToDelete: IExtension[] = []; const promises: Promise[] = []; for (const extension of e.extensions) { const key = this.getKey(extension.identifier, extension.version); @@ -115,7 +115,7 @@ export class ExtensionsWatcher extends Disposable { promises.push(this.extensionManagementService.scanInstalledExtensionAtLocation(extension.location) .then(result => { if (result) { - extensionsToUninstall.push(result); + extensionsToDelete.push(result); } else { this.logService.info('Extension not found at the location', extension.location.toString()); } @@ -125,8 +125,8 @@ export class ExtensionsWatcher extends Disposable { } try { await Promise.all(promises); - if (extensionsToUninstall.length) { - await this.uninstallExtensionsNotInProfiles(extensionsToUninstall); + if (extensionsToDelete.length) { + await this.deleteExtensionsNotInProfiles(extensionsToDelete); } } catch (error) { this.logService.error(error); @@ -180,13 +180,13 @@ export class ExtensionsWatcher extends Disposable { } } - private async uninstallExtensionsNotInProfiles(toUninstall?: IExtension[]): Promise { - if (!toUninstall) { + private async deleteExtensionsNotInProfiles(toDelete?: IExtension[]): Promise { + if (!toDelete) { const installed = await this.extensionManagementService.scanAllUserInstalledExtensions(); - toUninstall = installed.filter(installedExtension => !this.allExtensions.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); + toDelete = installed.filter(installedExtension => !this.allExtensions.has(this.getKey(installedExtension.identifier, installedExtension.manifest.version))); } - if (toUninstall.length) { - await this.extensionManagementService.markAsUninstalled(...toUninstall); + if (toDelete.length) { + await this.extensionManagementService.deleteExtensions(...toDelete); } } diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts index 74d3ffcd738..551ba576d44 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts @@ -224,31 +224,6 @@ suite('NativeExtensionsScanerService Test', () => { assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); }); - test('scan exclude uninstalled extensions', async () => { - await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); - await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub' })); - await instantiationService.get(IFileService).writeFile(joinPath(URI.file(instantiationService.get(INativeEnvironmentService).extensionsPath), '.obsolete'), VSBuffer.fromString(JSON.stringify({ 'pub.name2-1.0.0': true }))); - const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); - - const actual = await testObject.scanUserExtensions({}); - - assert.deepStrictEqual(actual.length, 1); - assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); - }); - - test('scan include uninstalled extensions', async () => { - await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); - await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub' })); - await instantiationService.get(IFileService).writeFile(joinPath(URI.file(instantiationService.get(INativeEnvironmentService).extensionsPath), '.obsolete'), VSBuffer.fromString(JSON.stringify({ 'pub.name2-1.0.0': true }))); - const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); - - const actual = await testObject.scanUserExtensions({ includeUninstalled: true }); - - assert.deepStrictEqual(actual.length, 2); - assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); - assert.deepStrictEqual(actual[1].identifier, { id: 'pub.name2' }); - }); - test('scan include invalid extensions', async () => { await aUserExtension(anExtensionManifest({ 'name': 'name', 'publisher': 'pub' })); await aUserExtension(anExtensionManifest({ 'name': 'name2', 'publisher': 'pub', engines: { vscode: '^1.67.0' } })); @@ -351,7 +326,7 @@ suite('ExtensionScannerInput', () => { ensureNoDisposablesAreLeakedInTestSuite(); test('compare inputs - location', () => { - const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(location, mtime, undefined, undefined, false, undefined, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(location, mtime, undefined, undefined, false, undefined, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, undefined), anInput(ROOT, undefined)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, 100), anInput(ROOT, 100)), true); @@ -361,7 +336,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - application location', () => { - const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(ROOT, undefined, location, mtime, false, undefined, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (location: URI, mtime: number | undefined) => new ExtensionScannerInput(ROOT, undefined, location, mtime, false, undefined, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, undefined), anInput(ROOT, undefined)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ROOT, 100), anInput(ROOT, 100)), true); @@ -371,7 +346,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - profile', () => { - const anInput = (profile: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, profile, profileScanOptions, ExtensionType.User, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (profile: boolean, profileScanOptions: IProfileExtensionsScanOptions | undefined) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, profile, profileScanOptions, ExtensionType.User, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(true, { bailOutWhenFileNotFound: true }), anInput(true, { bailOutWhenFileNotFound: true })), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(false, { bailOutWhenFileNotFound: true }), anInput(false, { bailOutWhenFileNotFound: true })), true); @@ -384,7 +359,7 @@ suite('ExtensionScannerInput', () => { }); test('compare inputs - extension type', () => { - const anInput = (type: ExtensionType) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, false, undefined, type, true, true, '1.1.1', undefined, undefined, true, undefined, {}); + const anInput = (type: ExtensionType) => new ExtensionScannerInput(ROOT, undefined, undefined, undefined, false, undefined, type, true, '1.1.1', undefined, undefined, true, undefined, {}); assert.strictEqual(ExtensionScannerInput.equals(anInput(ExtensionType.System), anInput(ExtensionType.System)), true); assert.strictEqual(ExtensionScannerInput.equals(anInput(ExtensionType.User), anInput(ExtensionType.User)), true); From 41be1c6b69760824c9fa42d4bcec5a406c41e9bb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2024 11:49:08 +0100 Subject: [PATCH 347/479] handle invalid withProgress invocation as external error (#236454) fixes https://github.com/Microsoft/vscode/issues/134892 --- src/vs/workbench/api/browser/mainThreadProgress.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadProgress.ts b/src/vs/workbench/api/browser/mainThreadProgress.ts index 6606b0d64bd..e6f4632b61b 100644 --- a/src/vs/workbench/api/browser/mainThreadProgress.ts +++ b/src/vs/workbench/api/browser/mainThreadProgress.ts @@ -9,6 +9,7 @@ import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions import { Action } from '../../../base/common/actions.js'; import { ICommandService } from '../../../platform/commands/common/commands.js'; import { localize } from '../../../nls.js'; +import { onUnexpectedExternalError } from '../../../base/common/errors.js'; class ManageExtensionAction extends Action { constructor(extensionId: string, label: string, commandService: ICommandService) { @@ -52,7 +53,13 @@ export class MainThreadProgress implements MainThreadProgressShape { options = notificationOptions; } - this._progressService.withProgress(options, task, () => this._proxy.$acceptProgressCanceled(handle)); + try { + this._progressService.withProgress(options, task, () => this._proxy.$acceptProgressCanceled(handle)); + } catch (err) { + // the withProgress-method will throw synchronously when invoked with bad options + // which is then an enternal/extension error + onUnexpectedExternalError(err); + } } $progressReport(handle: number, message: IProgressStep): void { From fca8bb2388430e67b42253199e87ed7fa325442a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 12:18:50 +0100 Subject: [PATCH 348/479] Deduplicate untitled workspaces with workspaces that restore (fix #234232) (#236457) --- .../electron-main/windowsMainService.ts | 58 ++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index f441f7d5fb7..d403852efcc 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -57,6 +57,7 @@ import { ILoggerMainService } from '../../log/electron-main/loggerService.js'; import { IAuxiliaryWindowsMainService } from '../../auxiliaryWindow/electron-main/auxiliaryWindows.js'; import { IAuxiliaryWindow } from '../../auxiliaryWindow/electron-main/auxiliaryWindow.js'; import { ICSSDevelopmentService } from '../../cssDev/node/cssDevService.js'; +import { ResourceSet } from '../../../base/common/map.js'; //#region Helper Interfaces @@ -729,7 +730,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private async getPathsToOpen(openConfig: IOpenConfiguration): Promise { let pathsToOpen: IPathToOpen[]; let isCommandLineOrAPICall = false; - let restoredWindows = false; + let isRestoringPaths = false; // Extract paths: from API if (openConfig.urisToOpen && openConfig.urisToOpen.length > 0) { @@ -759,19 +760,26 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic pathsToOpen.push(Object.create(null)); // add an empty window if we did not have windows to restore } - restoredWindows = true; + isRestoringPaths = true; } - // Convert multiple folders into workspace (if opened via API or CLI) - // This will ensure to open these folders in one window instead of multiple - // If we are in `addMode`, we should not do this because in that case all - // folders should be added to the existing window. + // Handle the case of multiple folders being opened from CLI while we are + // not in `--add` mode by creating an untitled workspace, only if: + // - they all share the same remote authority + // - there is no existing workspace to open that matches these folders if (!openConfig.addMode && isCommandLineOrAPICall) { - const foldersToOpen = pathsToOpen.filter(path => isSingleFolderWorkspacePathToOpen(path)) as ISingleFolderWorkspacePathToOpen[]; + const foldersToOpen = pathsToOpen.filter(path => isSingleFolderWorkspacePathToOpen(path)); if (foldersToOpen.length > 1) { const remoteAuthority = foldersToOpen[0].remoteAuthority; - if (foldersToOpen.every(folderToOpen => isEqualAuthority(folderToOpen.remoteAuthority, remoteAuthority))) { // only if all folder have the same authority - const workspace = await this.workspacesManagementMainService.createUntitledWorkspace(foldersToOpen.map(folder => ({ uri: folder.workspace.uri }))); + if (foldersToOpen.every(folderToOpen => isEqualAuthority(folderToOpen.remoteAuthority, remoteAuthority))) { + let workspace: IWorkspaceIdentifier | undefined; + + const lastSessionWorkspaceMatchingFolders = await this.doGetWorkspaceMatchingFoldersFromLastSession(remoteAuthority, foldersToOpen); + if (lastSessionWorkspaceMatchingFolders) { + workspace = lastSessionWorkspaceMatchingFolders; + } else { + workspace = await this.workspacesManagementMainService.createUntitledWorkspace(foldersToOpen.map(folder => ({ uri: folder.workspace.uri }))); + } // Add workspace and remove folders thereby pathsToOpen.push({ workspace, remoteAuthority }); @@ -780,12 +788,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } } - // Check for `window.startup` setting to include all windows + // Check for `window.restoreWindows` setting to include all windows // from the previous session if this is the initial startup and we have // not restored windows already otherwise. - // Use `unshift` to ensure any new window to open comes last - // for proper focus treatment. - if (openConfig.initialStartup && !restoredWindows && this.configurationService.getValue('window')?.restoreWindows === 'preserve') { + // Use `unshift` to ensure any new window to open comes last for proper + // focus treatment. + if (openConfig.initialStartup && !isRestoringPaths && this.configurationService.getValue('window')?.restoreWindows === 'preserve') { const lastSessionPaths = await this.doGetPathsFromLastSession(); pathsToOpen.unshift(...lastSessionPaths.filter(path => isWorkspacePathToOpen(path) || isSingleFolderWorkspacePathToOpen(path) || path.backupPath)); } @@ -974,6 +982,30 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return restoreWindows; } + private async doGetWorkspaceMatchingFoldersFromLastSession(remoteAuthority: string | undefined, folders: ISingleFolderWorkspacePathToOpen[]): Promise { + const workspaces = (await this.doGetPathsFromLastSession()).filter(path => isWorkspacePathToOpen(path)); + const folderUris = folders.map(folder => folder.workspace.uri); + + for (const { workspace } of workspaces) { + const resolvedWorkspace = await this.workspacesManagementMainService.resolveLocalWorkspace(workspace.configPath); + if ( + !resolvedWorkspace || + resolvedWorkspace.remoteAuthority !== remoteAuthority || + resolvedWorkspace.transient || + resolvedWorkspace.folders.length !== folders.length + ) { + continue; + } + + const folderSet = new ResourceSet(folderUris, uri => extUriBiasedIgnorePathCase.getComparisonKey(uri)); + if (resolvedWorkspace.folders.every(folder => folderSet.has(folder.uri))) { + return resolvedWorkspace; + } + } + + return undefined; + } + private async resolveOpenable(openable: IWindowOpenable, options: IPathResolveOptions = Object.create(null)): Promise { // handle file:// openables with some extra validation From 2f7beb56ff7319edc20b8f81c965da85738475e4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 12:46:34 +0100 Subject: [PATCH 349/479] Support for code page `1125` aka `cp866u` (fix #230438 (#236461) ) --- .../services/textfile/common/encoding.ts | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/services/textfile/common/encoding.ts b/src/vs/workbench/services/textfile/common/encoding.ts index b653dd7a847..599f2309e6a 100644 --- a/src/vs/workbench/services/textfile/common/encoding.ts +++ b/src/vs/workbench/services/textfile/common/encoding.ts @@ -615,151 +615,157 @@ export const SUPPORTED_ENCODINGS: EncodingsMap = { order: 20, guessableName: 'IBM866' }, + cp1125: { + labelLong: 'Cyrillic (CP 1125)', + labelShort: 'CP 1125', + order: 21, + guessableName: 'IBM1125' + }, iso88595: { labelLong: 'Cyrillic (ISO 8859-5)', labelShort: 'ISO 8859-5', - order: 21, + order: 22, guessableName: 'ISO-8859-5' }, koi8r: { labelLong: 'Cyrillic (KOI8-R)', labelShort: 'KOI8-R', - order: 22, + order: 23, guessableName: 'KOI8-R' }, koi8u: { labelLong: 'Cyrillic (KOI8-U)', labelShort: 'KOI8-U', - order: 23 + order: 24 }, iso885913: { labelLong: 'Estonian (ISO 8859-13)', labelShort: 'ISO 8859-13', - order: 24 + order: 25 }, windows1253: { labelLong: 'Greek (Windows 1253)', labelShort: 'Windows 1253', - order: 25, + order: 26, guessableName: 'windows-1253' }, iso88597: { labelLong: 'Greek (ISO 8859-7)', labelShort: 'ISO 8859-7', - order: 26, + order: 27, guessableName: 'ISO-8859-7' }, windows1255: { labelLong: 'Hebrew (Windows 1255)', labelShort: 'Windows 1255', - order: 27, + order: 28, guessableName: 'windows-1255' }, iso88598: { labelLong: 'Hebrew (ISO 8859-8)', labelShort: 'ISO 8859-8', - order: 28, + order: 29, guessableName: 'ISO-8859-8' }, iso885910: { labelLong: 'Nordic (ISO 8859-10)', labelShort: 'ISO 8859-10', - order: 29 + order: 30 }, iso885916: { labelLong: 'Romanian (ISO 8859-16)', labelShort: 'ISO 8859-16', - order: 30 + order: 31 }, windows1254: { labelLong: 'Turkish (Windows 1254)', labelShort: 'Windows 1254', - order: 31 + order: 32 }, iso88599: { labelLong: 'Turkish (ISO 8859-9)', labelShort: 'ISO 8859-9', - order: 32 + order: 33 }, windows1258: { labelLong: 'Vietnamese (Windows 1258)', labelShort: 'Windows 1258', - order: 33 + order: 34 }, gbk: { labelLong: 'Simplified Chinese (GBK)', labelShort: 'GBK', - order: 34 + order: 35 }, gb18030: { labelLong: 'Simplified Chinese (GB18030)', labelShort: 'GB18030', - order: 35 + order: 36 }, cp950: { labelLong: 'Traditional Chinese (Big5)', labelShort: 'Big5', - order: 36, + order: 37, guessableName: 'Big5' }, big5hkscs: { labelLong: 'Traditional Chinese (Big5-HKSCS)', labelShort: 'Big5-HKSCS', - order: 37 + order: 38 }, shiftjis: { labelLong: 'Japanese (Shift JIS)', labelShort: 'Shift JIS', - order: 38, + order: 39, guessableName: 'SHIFT_JIS' }, eucjp: { labelLong: 'Japanese (EUC-JP)', labelShort: 'EUC-JP', - order: 39, + order: 40, guessableName: 'EUC-JP' }, euckr: { labelLong: 'Korean (EUC-KR)', labelShort: 'EUC-KR', - order: 40, + order: 41, guessableName: 'EUC-KR' }, windows874: { labelLong: 'Thai (Windows 874)', labelShort: 'Windows 874', - order: 41 + order: 42 }, iso885911: { labelLong: 'Latin/Thai (ISO 8859-11)', labelShort: 'ISO 8859-11', - order: 42 + order: 43 }, koi8ru: { labelLong: 'Cyrillic (KOI8-RU)', labelShort: 'KOI8-RU', - order: 43 + order: 44 }, koi8t: { labelLong: 'Tajik (KOI8-T)', labelShort: 'KOI8-T', - order: 44 + order: 45 }, gb2312: { labelLong: 'Simplified Chinese (GB 2312)', labelShort: 'GB 2312', - order: 45, + order: 46, guessableName: 'GB2312' }, cp865: { labelLong: 'Nordic DOS (CP 865)', labelShort: 'CP 865', - order: 46 + order: 47 }, cp850: { labelLong: 'Western European DOS (CP 850)', labelShort: 'CP 850', - order: 47 + order: 48 } }; From 6c2faf1c5db3a438360b44a9f7b83bafac8efa0b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 13:06:31 +0100 Subject: [PATCH 350/479] chat - fix setup removal key (#236463) --- .../contrib/chat/browser/actions/chatActions.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 92f6c42dd3d..4cb15f40d68 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -531,13 +531,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, { submenu: MenuId.ChatCommandCenter, title: localize('title4', "Chat"), icon: Codicon.copilot, - when: ContextKeyExpr.and( - ContextKeyExpr.has('config.chat.commandCenter.enabled'), - ContextKeyExpr.or( - ChatContextKeys.Setup.installed, - ChatContextKeys.panelParticipantRegistered - ) - ), + when: ContextKeyExpr.has('config.chat.commandCenter.enabled'), order: 10001, }); @@ -547,13 +541,7 @@ registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction { 'chat.commandCenter.enabled', localize('toggle.chatControl', 'Copilot Controls'), localize('toggle.chatControlsDescription', "Toggle visibility of the Copilot Controls in title bar"), 4, false, - ContextKeyExpr.and( - ContextKeyExpr.has('config.window.commandCenter'), - ContextKeyExpr.or( - ChatContextKeys.Setup.installed, - ChatContextKeys.panelParticipantRegistered - ) - ) + ContextKeyExpr.has('config.window.commandCenter') ); } }); From 7579838b39047efb0f22236fea8e6d94ca6ea5e1 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 18 Dec 2024 14:12:34 +0100 Subject: [PATCH 351/479] Render the a background cover up only if really necessary (#236460) --- .../view/inlineEdits/sideBySideDiff.ts | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index a64c52dcd90..5f36bec5b33 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -14,6 +14,7 @@ import { ICommandService } from '../../../../../../platform/commands/common/comm import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { diffInserted, diffRemoved } from '../../../../../../platform/theme/common/colorRegistry.js'; import { darken, lighten, registerColor } from '../../../../../../platform/theme/common/colorUtils.js'; +import { IThemeService } from '../../../../../../platform/theme/common/themeService.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { Point } from '../../../../../browser/point.js'; @@ -108,7 +109,8 @@ export class InlineEditsSideBySideDiff extends Disposable { originalDisplayRange: LineRange; } | undefined>, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ICommandService private readonly _commandService: ICommandService + @ICommandService private readonly _commandService: ICommandService, + @IThemeService private readonly _themeService: IThemeService, ) { super(); @@ -487,19 +489,33 @@ export class InlineEditsSideBySideDiff extends Disposable { return extendedModifiedPathBuilder.build(); }); + private readonly _originalBackgroundColor = observableFromEvent(this, this._themeService.onDidColorThemeChange, () => { + return this._themeService.getColorTheme().getColor(originalBackgroundColor) ?? Color.transparent; + }); + private readonly _backgroundSvg = n.svg({ transform: 'translate(-0.5 -0.5)', style: { overflow: 'visible', pointerEvents: 'none', position: 'absolute' }, }, [ n.svgElem('path', { class: 'rightOfModifiedBackgroundCoverUp', - d: this._previewEditorLayoutInfo.map(layoutInfo => layoutInfo && new PathBuilder() - .moveTo(layoutInfo.code1) - .lineTo(layoutInfo.code1.deltaX(1000)) - .lineTo(layoutInfo.code2.deltaX(1000)) - .lineTo(layoutInfo.code2) - .build() - ), + d: derived(reader => { + const layoutInfo = this._previewEditorLayoutInfo.read(reader); + if (!layoutInfo) { + return undefined; + } + const originalBackgroundColor = this._originalBackgroundColor.read(reader); + if (originalBackgroundColor.isTransparent()) { + return undefined; + } + + return new PathBuilder() + .moveTo(layoutInfo.code1) + .lineTo(layoutInfo.code1.deltaX(1000)) + .lineTo(layoutInfo.code2.deltaX(1000)) + .lineTo(layoutInfo.code2) + .build(); + }), style: { fill: 'var(--vscode-editor-background, transparent)', } From 473620cdd2f4f38e3e4f409d96aeeb02d81db5dc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 18 Dec 2024 14:14:46 +0100 Subject: [PATCH 352/479] Wrong use of disable store in searchResultsView.ts (fix #236456) (#236469) --- .../workbench/contrib/search/browser/searchResultsView.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 40df97b78cb..a4705ea7f15 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -192,7 +192,7 @@ export class FolderMatchRenderer extends Disposable implements ICompressibleTree SearchContext.FileFocusKey.bindTo(contextKeyServiceMain).set(false); SearchContext.FolderFocusKey.bindTo(contextKeyServiceMain).set(true); - const instantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain]))); + const instantiationService = disposables.add(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain]))); const actions = disposables.add(instantiationService.createInstance(MenuWorkbenchToolBar, actionBarContainer, MenuId.SearchActionMenu, { menuOptions: { shouldForwardArgs: true @@ -292,7 +292,7 @@ export class FileMatchRenderer extends Disposable implements ICompressibleTreeRe SearchContext.FileFocusKey.bindTo(contextKeyServiceMain).set(true); SearchContext.FolderFocusKey.bindTo(contextKeyServiceMain).set(false); - const instantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain]))); + const instantiationService = disposables.add(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain]))); const actions = disposables.add(instantiationService.createInstance(MenuWorkbenchToolBar, actionBarContainer, MenuId.SearchActionMenu, { menuOptions: { shouldForwardArgs: true @@ -384,7 +384,7 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender SearchContext.FileFocusKey.bindTo(contextKeyServiceMain).set(false); SearchContext.FolderFocusKey.bindTo(contextKeyServiceMain).set(false); - const instantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain]))); + const instantiationService = disposables.add(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain]))); const actions = disposables.add(instantiationService.createInstance(MenuWorkbenchToolBar, actionBarContainer, MenuId.SearchActionMenu, { menuOptions: { shouldForwardArgs: true From b274aac2ca213a2ca9b9626b7060328f31cfffaf Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:46:43 +0100 Subject: [PATCH 353/479] Allow center layout with vertical groups (#236471) allow center layout with vertical groups --- src/vs/workbench/browser/layout.ts | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 84e2eaadb3c..592bb4146af 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -23,14 +23,14 @@ import { getMenuBarVisibility, IPath, hasNativeTitlebar, hasCustomTitlebar, Titl import { IHostService } from '../services/host/browser/host.js'; import { IBrowserWorkbenchEnvironmentService } from '../services/environment/browser/environmentService.js'; import { IEditorService } from '../services/editor/common/editorService.js'; -import { EditorGroupLayout, GroupsOrder, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js'; +import { EditorGroupLayout, GroupOrientation, GroupsOrder, IEditorGroupsService } from '../services/editor/common/editorGroupsService.js'; import { SerializableGrid, ISerializableView, ISerializedGrid, Orientation, ISerializedNode, ISerializedLeafNode, Direction, IViewSize, Sizing } from '../../base/browser/ui/grid/grid.js'; import { Part } from './part.js'; import { IStatusbarService } from '../services/statusbar/browser/statusbar.js'; import { IFileService } from '../../platform/files/common/files.js'; import { isCodeEditor } from '../../editor/browser/editorBrowser.js'; import { coalesce } from '../../base/common/arrays.js'; -import { assertIsDefined } from '../../base/common/types.js'; +import { assertIsDefined, isDefined } from '../../base/common/types.js'; import { INotificationService, NotificationsFilter } from '../../platform/notification/common/notification.js'; import { IThemeService } from '../../platform/theme/common/themeService.js'; import { WINDOW_ACTIVE_BORDER, WINDOW_INACTIVE_BORDER } from '../common/theme.js'; @@ -1610,19 +1610,28 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi centerMainEditorLayout(active: boolean, skipLayout?: boolean): void { this.stateModel.setRuntimeValue(LayoutStateKeys.MAIN_EDITOR_CENTERED, active); - const activeMainEditor = this.mainPartEditorService.activeEditor; + const mainVisibleEditors = this.editorGroupService.mainPart.groups.map(g => g.activeEditor).filter(isDefined); + const isEditorComplex = mainVisibleEditors.some(editor => { + if (editor instanceof DiffEditorInput) { + return this.configurationService.getValue('diffEditor.renderSideBySide'); + } else if (editor?.hasCapability(EditorInputCapabilities.MultipleEditors)) { + return true; + } + return false; + }); - let isEditorComplex = false; - if (activeMainEditor instanceof DiffEditorInput) { - isEditorComplex = this.configurationService.getValue('diffEditor.renderSideBySide'); - } else if (activeMainEditor?.hasCapability(EditorInputCapabilities.MultipleEditors)) { - isEditorComplex = true; + const layout = this.editorGroupService.getLayout(); + let hasMoreThanOneColumn = false; + if (layout.orientation === GroupOrientation.HORIZONTAL) { + hasMoreThanOneColumn = layout.groups.length > 1; + } else { + hasMoreThanOneColumn = layout.groups.some(g => g.groups && g.groups.length > 1); } const isCenteredLayoutAutoResizing = this.configurationService.getValue('workbench.editor.centeredLayoutAutoResize'); if ( isCenteredLayoutAutoResizing && - ((this.editorGroupService.mainPart.groups.length > 1 && !this.editorGroupService.mainPart.hasMaximizedGroup()) || isEditorComplex) + ((hasMoreThanOneColumn && !this.editorGroupService.mainPart.hasMaximizedGroup()) || isEditorComplex) ) { active = false; // disable centered layout for complex editors or when there is more than one group } From 1efa0d219662095a77139bb0d18d664ee6230f3b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 18 Dec 2024 14:59:40 +0100 Subject: [PATCH 354/479] refactor: enhance sync method and reuse for preview and some clean up (#236470) * refactor: enhance sync method and reuse for preview and some clean up * fix tests --- .../common/abstractSynchronizer.ts | 99 +++--- .../userDataSync/common/extensionsSync.ts | 3 +- .../userDataSync/common/globalStateSync.ts | 30 +- .../userDataSync/common/userDataSync.ts | 8 +- .../common/userDataSyncService.ts | 41 +-- .../test/common/keybindingsSync.test.ts | 6 +- .../test/common/settingsSync.test.ts | 2 +- .../test/common/snippetsSync.test.ts | 155 ++------- .../test/common/synchronizer.test.ts | 305 ++---------------- .../test/common/tasksSync.test.ts | 2 +- .../browser/editSessions.contribution.ts | 8 +- .../editSessions/common/workspaceStateSync.ts | 9 +- 12 files changed, 166 insertions(+), 502 deletions(-) diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index dc3b9950ad7..11c0315468a 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -26,7 +26,14 @@ import { getServiceMachineId } from '../../externalServices/common/serviceMachin import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js'; -import { Change, getLastSyncResourceUri, IRemoteUserData, IResourcePreview as IBaseResourcePreview, ISyncData, IUserDataSyncResourcePreview as IBaseSyncResourcePreview, IUserData, IUserDataSyncResourceInitializer, IUserDataSyncLocalStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, MergeState, PREVIEW_DIR_NAME, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME, IUserDataResourceManifest, getPathSegments, IUserDataSyncResourceConflicts, IUserDataSyncResource } from './userDataSync.js'; +import { + Change, getLastSyncResourceUri, IRemoteUserData, IResourcePreview as IBaseResourcePreview, ISyncData, + IUserDataSyncResourcePreview as IBaseSyncResourcePreview, IUserData, IUserDataSyncResourceInitializer, IUserDataSyncLocalStoreService, + IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, + IUserDataSyncUtilService, MergeState, PREVIEW_DIR_NAME, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, + USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME, IUserDataResourceManifest, getPathSegments, IUserDataSyncResourceConflicts, + IUserDataSyncResource, IUserDataSyncResourcePreview +} from './userDataSync.js'; import { IUserDataProfile, IUserDataProfilesService } from '../../userDataProfile/common/userDataProfile.js'; type IncompatibleSyncSourceClassification = { @@ -114,6 +121,12 @@ interface ILastSyncUserDataState { [key: string]: any; } +export const enum SyncStrategy { + Preview = 'preview', // Merge the local and remote data without applying. + Merge = 'merge', // Merge the local and remote data and apply. + PullOrPush = 'pull-push', // Pull the remote data or push the local data. +} + export abstract class AbstractSynchroniser extends Disposable implements IUserDataSynchroniser { private syncPreviewPromise: CancelablePromise | null = null; @@ -180,7 +193,7 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa this.logService.info(`${this.syncResourceLogLabel}: In conflicts state and local change detected. Syncing again...`); const preview = await this.syncPreviewPromise!; this.syncPreviewPromise = null; - const status = await this.performSync(preview.remoteUserData, preview.lastSyncUserData, true, this.getUserDataSyncConfiguration()); + const status = await this.performSync(preview.remoteUserData, preview.lastSyncUserData, SyncStrategy.Merge, this.getUserDataSyncConfiguration()); this.setStatus(status); } @@ -202,28 +215,7 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa } } - async sync(manifest: IUserDataResourceManifest | null, headers: IHeaders = {}): Promise { - await this._sync(manifest, true, this.getUserDataSyncConfiguration(), headers); - } - - async preview(manifest: IUserDataResourceManifest | null, userDataSyncConfiguration: IUserDataSyncConfiguration, headers: IHeaders = {}): Promise { - return this._sync(manifest, false, userDataSyncConfiguration, headers); - } - - async apply(force: boolean, headers: IHeaders = {}): Promise { - try { - this.syncHeaders = { ...headers }; - - const status = await this.doApply(force); - this.setStatus(status); - - return this.syncPreviewPromise; - } finally { - this.syncHeaders = {}; - } - } - - private async _sync(manifest: IUserDataResourceManifest | null, apply: boolean, userDataSyncConfiguration: IUserDataSyncConfiguration, headers: IHeaders): Promise { + async sync(manifest: IUserDataResourceManifest | null, preview: boolean = false, userDataSyncConfiguration: IUserDataSyncConfiguration = this.getUserDataSyncConfiguration(), headers: IHeaders = {}): Promise { try { this.syncHeaders = { ...headers }; @@ -244,7 +236,7 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa try { const lastSyncUserData = await this.getLastSyncUserData(); const remoteUserData = await this.getLatestRemoteUserData(manifest, lastSyncUserData); - status = await this.performSync(remoteUserData, lastSyncUserData, apply, userDataSyncConfiguration); + status = await this.performSync(remoteUserData, lastSyncUserData, preview ? SyncStrategy.Preview : SyncStrategy.Merge, userDataSyncConfiguration); if (status === SyncStatus.HasConflicts) { this.logService.info(`${this.syncResourceLogLabel}: Detected conflicts while synchronizing ${this.resource.toLowerCase()}.`); } else if (status === SyncStatus.Idle) { @@ -259,6 +251,19 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa } } + async apply(force: boolean, headers: IHeaders = {}): Promise { + try { + this.syncHeaders = { ...headers }; + + const status = await this.doApply(force); + this.setStatus(status); + + return this.syncPreviewPromise; + } finally { + this.syncHeaders = {}; + } + } + async replace(content: string): Promise { const syncData = this.parseSyncData(content); if (!syncData) { @@ -318,7 +323,7 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa return this.getRemoteUserData(lastSyncUserData); } - private async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, apply: boolean, userDataSyncConfiguration: IUserDataSyncConfiguration): Promise { + private async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, strategy: SyncStrategy, userDataSyncConfiguration: IUserDataSyncConfiguration): Promise { if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) { // current version is not compatible with cloud version this.telemetryService.publicLog2<{ source: string }, IncompatibleSyncSourceClassification>('sync/incompatible', { source: this.resource }); @@ -326,7 +331,7 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa } try { - return await this.doSync(remoteUserData, lastSyncUserData, apply, userDataSyncConfiguration); + return await this.doSync(remoteUserData, lastSyncUserData, strategy, userDataSyncConfiguration); } catch (e) { if (e instanceof UserDataSyncError) { switch (e.code) { @@ -334,7 +339,7 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa case UserDataSyncErrorCode.LocalPreconditionFailed: // Rejected as there is a new local version. Syncing again... this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize ${this.syncResourceLogLabel} as there is a new local version available. Synchronizing again...`); - return this.performSync(remoteUserData, lastSyncUserData, apply, userDataSyncConfiguration); + return this.performSync(remoteUserData, lastSyncUserData, strategy, userDataSyncConfiguration); case UserDataSyncErrorCode.Conflict: case UserDataSyncErrorCode.PreconditionFailed: @@ -348,19 +353,20 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa // and one of them successfully updated remote and last sync state. lastSyncUserData = await this.getLastSyncUserData(); - return this.performSync(remoteUserData, lastSyncUserData, apply, userDataSyncConfiguration); + return this.performSync(remoteUserData, lastSyncUserData, SyncStrategy.Merge, userDataSyncConfiguration); } } throw e; } } - protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, apply: boolean, userDataSyncConfiguration: IUserDataSyncConfiguration): Promise { + protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, strategy: SyncStrategy, userDataSyncConfiguration: IUserDataSyncConfiguration): Promise { try { const isRemoteDataFromCurrentMachine = await this.isRemoteDataFromCurrentMachine(remoteUserData); const acceptRemote = !isRemoteDataFromCurrentMachine && lastSyncUserData === null && this.getStoredLastSyncUserDataStateContent() !== undefined; - const merge = apply && !acceptRemote; + const merge = strategy === SyncStrategy.Preview || (strategy === SyncStrategy.Merge && !acceptRemote); + const apply = strategy === SyncStrategy.Merge || strategy === SyncStrategy.PullOrPush; // generate or use existing preview if (!this.syncPreviewPromise) { @@ -369,13 +375,26 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa let preview = await this.syncPreviewPromise; - if (apply && acceptRemote) { + if (strategy === SyncStrategy.Merge && acceptRemote) { this.logService.info(`${this.syncResourceLogLabel}: Accepting remote because it was synced before and the last sync data is not available.`); for (const resourcePreview of preview.resourcePreviews) { preview = (await this.accept(resourcePreview.remoteResource)) || preview; } } + else if (strategy === SyncStrategy.PullOrPush) { + for (const resourcePreview of preview.resourcePreviews) { + if (resourcePreview.mergeState === MergeState.Accepted) { + continue; + } + if (remoteUserData.ref === lastSyncUserData?.ref || isRemoteDataFromCurrentMachine) { + preview = (await this.accept(resourcePreview.localResource)) ?? preview; + } else { + preview = (await this.accept(resourcePreview.remoteResource)) ?? preview; + } + } + } + this.updateConflicts(preview.resourcePreviews); if (preview.resourcePreviews.some(({ mergeState }) => mergeState === MergeState.Conflict)) { return SyncStatus.HasConflicts; @@ -396,22 +415,6 @@ export abstract class AbstractSynchroniser extends Disposable implements IUserDa } } - async merge(resource: URI): Promise { - await this.updateSyncResourcePreview(resource, async (resourcePreview) => { - const mergeResult = await this.getMergeResult(resourcePreview, CancellationToken.None); - await this.fileService.writeFile(resourcePreview.previewResource, VSBuffer.fromString(mergeResult?.content || '')); - const acceptResult: IAcceptResult | undefined = mergeResult && !mergeResult.hasConflicts - ? await this.getAcceptResult(resourcePreview, resourcePreview.previewResource, undefined, CancellationToken.None) - : undefined; - resourcePreview.acceptResult = acceptResult; - resourcePreview.mergeState = mergeResult.hasConflicts ? MergeState.Conflict : acceptResult ? MergeState.Accepted : MergeState.Preview; - resourcePreview.localChange = acceptResult ? acceptResult.localChange : mergeResult.localChange; - resourcePreview.remoteChange = acceptResult ? acceptResult.remoteChange : mergeResult.remoteChange; - return resourcePreview; - }); - return this.syncPreviewPromise; - } - async accept(resource: URI, content?: string | null): Promise { await this.updateSyncResourcePreview(resource, async (resourcePreview) => { const acceptResult = await this.getAcceptResult(resourcePreview, resource, content, CancellationToken.None); diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 4054babf6f0..2269f4e3c58 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -242,7 +242,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse private async acceptLocal(resourcePreview: IExtensionResourcePreview): Promise { const installedExtensions = await this.extensionManagementService.getInstalled(undefined, this.syncResource.profile.extensionsResource); const ignoredExtensions = this.ignoredExtensionsManagementService.getIgnoredExtensions(installedExtensions); - const mergeResult = merge(resourcePreview.localExtensions, null, null, resourcePreview.skippedExtensions, ignoredExtensions, resourcePreview.builtinExtensions); + const remoteExtensions = resourcePreview.remoteContent ? JSON.parse(resourcePreview.remoteContent) : null; + const mergeResult = merge(resourcePreview.localExtensions, remoteExtensions, remoteExtensions, resourcePreview.skippedExtensions, ignoredExtensions, resourcePreview.builtinExtensions); const { local, remote } = mergeResult; return { content: resourcePreview.localContent, diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 75ea2446438..e37893825af 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -194,25 +194,37 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } private async acceptLocal(resourcePreview: IGlobalStateResourcePreview): Promise { - return { - content: resourcePreview.localContent, - local: { added: {}, removed: [], updated: {} }, - remote: { added: Object.keys(resourcePreview.localUserData.storage), removed: [], updated: [], all: resourcePreview.localUserData.storage }, - localChange: Change.None, - remoteChange: Change.Modified, - }; + if (resourcePreview.remoteContent !== null) { + const remoteGlobalState: IGlobalState = JSON.parse(resourcePreview.remoteContent); + const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, remoteGlobalState.storage, resourcePreview.storageKeys, this.logService); + return { + content: resourcePreview.remoteContent, + local, + remote, + localChange: Change.None, + remoteChange: remote.all !== null ? Change.Modified : Change.None, + }; + } else { + return { + content: resourcePreview.localContent, + local: { added: {}, removed: [], updated: {} }, + remote: { added: Object.keys(resourcePreview.localUserData.storage), removed: [], updated: [], all: resourcePreview.localUserData.storage }, + localChange: Change.None, + remoteChange: Change.Modified, + }; + } } private async acceptRemote(resourcePreview: IGlobalStateResourcePreview): Promise { if (resourcePreview.remoteContent !== null) { const remoteGlobalState: IGlobalState = JSON.parse(resourcePreview.remoteContent); - const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, resourcePreview.storageKeys, this.logService); + const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, resourcePreview.localUserData.storage, resourcePreview.storageKeys, this.logService); return { content: resourcePreview.remoteContent, local, remote, localChange: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0 ? Change.Modified : Change.None, - remoteChange: remote !== null ? Change.Modified : Change.None, + remoteChange: Change.None, }; } else { return { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index c3224e18bdd..02824fa3467 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -498,14 +498,10 @@ export interface IUserDataSynchroniser { readonly onDidChangeLocal: Event; - sync(manifest: IUserDataResourceManifest | null, headers: IHeaders): Promise; - stop(): Promise; - - preview(manifest: IUserDataResourceManifest | null, userDataSyncConfiguration: IUserDataSyncConfiguration, headers: IHeaders): Promise; + sync(manifest: IUserDataResourceManifest | null, preview: boolean, userDataSyncConfiguration: IUserDataSyncConfiguration, headers: IHeaders): Promise; accept(resource: URI, content?: string | null): Promise; - merge(resource: URI): Promise; - discard(resource: URI): Promise; apply(force: boolean, headers: IHeaders): Promise; + stop(): Promise; hasPreviouslySynced(): Promise; hasLocalData(): Promise; diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index a18be8e3996..46a443581b9 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -28,10 +28,10 @@ import { SnippetsSynchroniser } from './snippetsSync.js'; import { TasksSynchroniser } from './tasksSync.js'; import { UserDataProfilesManifestSynchroniser } from './userDataProfilesManifestSync.js'; import { - ALL_SYNC_RESOURCES, Change, createSyncHeaders, IUserDataManualSyncTask, IUserDataSyncResourceConflicts, IUserDataSyncResourceError, + ALL_SYNC_RESOURCES, createSyncHeaders, IUserDataManualSyncTask, IUserDataSyncResourceConflicts, IUserDataSyncResourceError, IUserDataSyncResource, ISyncResourceHandle, IUserDataSyncTask, ISyncUserDataProfile, IUserDataManifest, IUserDataResourceManifest, IUserDataSyncConfiguration, IUserDataSyncEnablementService, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, - MergeState, SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, UserDataSyncStoreError, USER_DATA_SYNC_CONFIGURATION_SCOPE, IUserDataSyncResourceProviderService, IUserDataActivityData, IUserDataSyncLocalStoreService + SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, UserDataSyncStoreError, USER_DATA_SYNC_CONFIGURATION_SCOPE, IUserDataSyncResourceProviderService, IUserDataActivityData, IUserDataSyncLocalStoreService, } from './userDataSync.js'; type SyncErrorClassification = { @@ -200,7 +200,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ }; } - private async sync(manifest: IUserDataManifest | null, merge: boolean, executionId: string, token: CancellationToken): Promise { + private async sync(manifest: IUserDataManifest | null, preview: boolean, executionId: string, token: CancellationToken): Promise { this._syncErrors = []; try { if (this.status !== SyncStatus.HasConflicts) { @@ -209,7 +209,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ // Sync Default Profile First const defaultProfileSynchronizer = this.getOrCreateActiveProfileSynchronizer(this.userDataProfilesService.defaultProfile, undefined); - this._syncErrors.push(...await this.syncProfile(defaultProfileSynchronizer, manifest, merge, executionId, token)); + this._syncErrors.push(...await this.syncProfile(defaultProfileSynchronizer, manifest, preview, executionId, token)); // Sync other profiles const userDataProfileManifestSynchronizer = defaultProfileSynchronizer.enabled.find(s => s.resource === SyncResource.Profiles); @@ -218,7 +218,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ if (token.isCancellationRequested) { return; } - await this.syncRemoteProfiles(syncProfiles, manifest, merge, executionId, token); + await this.syncRemoteProfiles(syncProfiles, manifest, preview, executionId, token); } } finally { if (this.status !== SyncStatus.HasConflicts) { @@ -228,7 +228,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } } - private async syncRemoteProfiles(remoteProfiles: ISyncUserDataProfile[], manifest: IUserDataManifest | null, merge: boolean, executionId: string, token: CancellationToken): Promise { + private async syncRemoteProfiles(remoteProfiles: ISyncUserDataProfile[], manifest: IUserDataManifest | null, preview: boolean, executionId: string, token: CancellationToken): Promise { for (const syncProfile of remoteProfiles) { if (token.isCancellationRequested) { return; @@ -240,7 +240,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } this.logService.info('Syncing profile.', syncProfile.name); const profileSynchronizer = this.getOrCreateActiveProfileSynchronizer(profile, syncProfile); - this._syncErrors.push(...await this.syncProfile(profileSynchronizer, manifest, merge, executionId, token)); + this._syncErrors.push(...await this.syncProfile(profileSynchronizer, manifest, preview, executionId, token)); } // Dispose & Delete profile synchronizers which do not exist anymore for (const [key, profileSynchronizerItem] of this.activeProfileSynchronizers.entries()) { @@ -285,8 +285,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } } - private async syncProfile(profileSynchronizer: ProfileSynchronizer, manifest: IUserDataManifest | null, merge: boolean, executionId: string, token: CancellationToken): Promise { - const errors = await profileSynchronizer.sync(manifest, merge, executionId, token); + private async syncProfile(profileSynchronizer: ProfileSynchronizer, manifest: IUserDataManifest | null, preview: boolean, executionId: string, token: CancellationToken): Promise { + const errors = await profileSynchronizer.sync(manifest, preview, executionId, token); return errors.map(([syncResource, error]) => ({ profile: profileSynchronizer.profile, syncResource, error })); } @@ -715,7 +715,7 @@ class ProfileSynchronizer extends Disposable { } } - async sync(manifest: IUserDataManifest | null, merge: boolean, executionId: string, token: CancellationToken): Promise<[SyncResource, UserDataSyncError][]> { + async sync(manifest: IUserDataManifest | null, preview: boolean, executionId: string, token: CancellationToken): Promise<[SyncResource, UserDataSyncError][]> { // Return if cancellation is requested if (token.isCancellationRequested) { @@ -731,7 +731,7 @@ class ProfileSynchronizer extends Disposable { const syncErrors: [SyncResource, UserDataSyncError][] = []; const syncHeaders = createSyncHeaders(executionId); const resourceManifest: IUserDataResourceManifest | null = (this.collection ? manifest?.collections?.[this.collection]?.latest : manifest?.latest) ?? null; - const userDataSyncConfiguration = merge ? await this.getUserDataSyncConfiguration(resourceManifest) : {}; + const userDataSyncConfiguration = preview ? await this.getUserDataSyncConfiguration(resourceManifest) : this.getLocalUserDataSyncConfiguration(); for (const synchroniser of synchronizers) { // Return if cancellation is requested if (token.isCancellationRequested) { @@ -744,18 +744,7 @@ class ProfileSynchronizer extends Disposable { } try { - if (merge) { - const preview = await synchroniser.preview(resourceManifest, userDataSyncConfiguration, syncHeaders); - if (preview) { - for (const resourcePreview of preview.resourcePreviews) { - if ((resourcePreview.localChange !== Change.None || resourcePreview.remoteChange !== Change.None) && resourcePreview.mergeState === MergeState.Preview) { - await synchroniser.merge(resourcePreview.previewResource); - } - } - } - } else { - await synchroniser.sync(resourceManifest, syncHeaders); - } + await synchroniser.sync(resourceManifest, preview, userDataSyncConfiguration, syncHeaders); } catch (e) { const userDataSyncError = UserDataSyncError.toUserDataSyncError(e); reportUserDataSyncError(userDataSyncError, executionId, this.userDataSyncStoreManagementService, this.telemetryService); @@ -825,7 +814,7 @@ class ProfileSynchronizer extends Disposable { if (!this.profile.isDefault) { return {}; } - const local = this.configurationService.getValue(USER_DATA_SYNC_CONFIGURATION_SCOPE); + const local = this.getLocalUserDataSyncConfiguration(); const settingsSynchronizer = this.enabled.find(synchronizer => synchronizer instanceof SettingsSynchroniser); if (settingsSynchronizer) { const remote = await (settingsSynchronizer).getRemoteUserDataSyncConfiguration(manifest); @@ -834,6 +823,10 @@ class ProfileSynchronizer extends Disposable { return local; } + private getLocalUserDataSyncConfiguration(): IUserDataSyncConfiguration { + return this.configurationService.getValue(USER_DATA_SYNC_CONFIGURATION_SCOPE); + } + private setStatus(status: SyncStatus): void { if (this._status !== status) { this._status = status; diff --git a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts index 00841916bbe..c0103898ddc 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts @@ -196,11 +196,11 @@ suite('KeybindingsSync', () => { await fileService.del(keybindingsResource); } - const preview = (await testObject.preview(await client.getResourceManifest(), {}))!; + const preview = await testObject.sync(await client.getResourceManifest(), true); server.reset(); - const content = await testObject.resolveContent(preview.resourcePreviews[0].remoteResource); - await testObject.accept(preview.resourcePreviews[0].remoteResource, content); + const content = await testObject.resolveContent(preview!.resourcePreviews[0].remoteResource); + await testObject.accept(preview!.resourcePreviews[0].remoteResource, content); await testObject.apply(false); assert.deepStrictEqual(server.requests, []); }); diff --git a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts index 914ae83a5d4..b3c6eee9926 100644 --- a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts @@ -569,7 +569,7 @@ suite('SettingsSync - Manual', () => { }`; await updateSettings(settingsContent, client); - let preview = await testObject.preview(await client.getResourceManifest(), {}); + let preview = await testObject.sync(await client.getResourceManifest(), true); assert.strictEqual(testObject.status, SyncStatus.Syncing); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); preview = await testObject.apply(false); diff --git a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts index 89e9c59c48b..0e37d04d493 100644 --- a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts @@ -674,49 +674,12 @@ suite('SnippetsSync', () => { assert.ok(!await fileService.exists(dirname(conflicts[0].previewResource))); }); - test('merge when there are multiple snippets and only one snippet is merged', async () => { - const environmentService = testClient.instantiationService.get(IEnvironmentService); - - await updateSnippet('html.json', htmlSnippet2, testClient); - await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - - preview = await testObject.merge(preview!.resourcePreviews[0].localResource); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - }); - test('merge when there are multiple snippets and all snippets are merged', async () => { const environmentService = testClient.instantiationService.get(IEnvironmentService); await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - - preview = await testObject.merge(preview!.resourcePreviews[0].localResource); - preview = await testObject.merge(preview!.resourcePreviews[1].localResource); + const preview = await testObject.sync(await testClient.getResourceManifest(), true); assert.strictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, @@ -728,22 +691,9 @@ suite('SnippetsSync', () => { }); test('merge when there are multiple snippets and all snippets are merged and applied', async () => { - const environmentService = testClient.instantiationService.get(IEnvironmentService); - await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - - preview = await testObject.merge(preview!.resourcePreviews[0].localResource); - preview = await testObject.merge(preview!.resourcePreviews[1].localResource); + let preview = await testObject.sync(await testClient.getResourceManifest(), true); preview = await testObject.apply(false); assert.strictEqual(testObject.status, SyncStatus.Idle); @@ -759,17 +709,7 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet1, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - - preview = await testObject.merge(preview!.resourcePreviews[0].localResource); + const preview = await testObject.sync(await testClient.getResourceManifest(), true); assert.strictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, @@ -780,25 +720,14 @@ suite('SnippetsSync', () => { assert.deepStrictEqual(testObject.conflicts.conflicts, []); }); - test('merge when there are multiple snippets and one snippet has no changes and one snippet is merged and applied', async () => { - const environmentService = testClient.instantiationService.get(IEnvironmentService); - + test('merge when there are multiple snippets and one snippet has no changes and snippets is merged and applied', async () => { await updateSnippet('html.json', htmlSnippet1, client2); await client2.sync(); await updateSnippet('html.json', htmlSnippet1, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); + let preview = await testObject.sync(await testClient.getResourceManifest(), true); - preview = await testObject.merge(preview!.resourcePreviews[0].localResource); preview = await testObject.apply(false); assert.strictEqual(testObject.status, SyncStatus.Idle); @@ -806,7 +735,7 @@ suite('SnippetsSync', () => { assert.deepStrictEqual(testObject.conflicts.conflicts, []); }); - test('merge when there are multiple snippets with conflicts and only one snippet is merged', async () => { + test('merge when there are multiple snippets with conflicts and all snippets are merged', async () => { const environmentService = testClient.instantiationService.get(IEnvironmentService); await updateSnippet('html.json', htmlSnippet1, client2); @@ -815,17 +744,7 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + const preview = await testObject.sync(await testClient.getResourceManifest(), true); assert.strictEqual(testObject.status, SyncStatus.HasConflicts); assertPreviews(preview!.resourcePreviews, @@ -836,10 +755,11 @@ suite('SnippetsSync', () => { assertPreviews(testObject.conflicts.conflicts, [ joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), ]); }); - test('merge when there are multiple snippets with conflicts and all snippets are merged', async () => { + test('accept when there are multiple snippets with conflicts and only one snippet is accepted', async () => { const environmentService = testClient.instantiationService.get(IEnvironmentService); await updateSnippet('html.json', htmlSnippet1, client2); @@ -848,18 +768,7 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, - [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), - ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); - preview = await testObject.merge(preview!.resourcePreviews[1].previewResource); + let preview = await testObject.sync(await testClient.getResourceManifest(), true); assert.strictEqual(testObject.status, SyncStatus.HasConflicts); assertPreviews(preview!.resourcePreviews, @@ -872,36 +781,19 @@ suite('SnippetsSync', () => { joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), ]); - }); - test('accept when there are multiple snippets with conflicts and only one snippet is accepted', async () => { - const environmentService = testClient.instantiationService.get(IEnvironmentService); - - await updateSnippet('html.json', htmlSnippet1, client2); - await updateSnippet('typescript.json', tsSnippet1, client2); - await client2.sync(); - - await updateSnippet('html.json', htmlSnippet2, testClient); - await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); + preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, htmlSnippet2); - assert.strictEqual(testObject.status, SyncStatus.Syncing); + assert.strictEqual(testObject.status, SyncStatus.HasConflicts); assertPreviews(preview!.resourcePreviews, [ joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); - - preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, htmlSnippet2); - - assert.strictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, + assertPreviews(testObject.conflicts.conflicts, [ - joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); }); test('accept when there are multiple snippets with conflicts and all snippets are accepted', async () => { @@ -913,15 +805,19 @@ suite('SnippetsSync', () => { await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); + let preview = await testObject.sync(await testClient.getResourceManifest(), true); - assert.strictEqual(testObject.status, SyncStatus.Syncing); + assert.strictEqual(testObject.status, SyncStatus.HasConflicts); assertPreviews(preview!.resourcePreviews, [ joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); + assertPreviews(testObject.conflicts.conflicts, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, htmlSnippet2); preview = await testObject.accept(preview!.resourcePreviews[1].previewResource, tsSnippet2); @@ -937,22 +833,25 @@ suite('SnippetsSync', () => { test('accept when there are multiple snippets with conflicts and all snippets are accepted and applied', async () => { const environmentService = testClient.instantiationService.get(IEnvironmentService); - await updateSnippet('html.json', htmlSnippet1, client2); await updateSnippet('typescript.json', tsSnippet1, client2); await client2.sync(); await updateSnippet('html.json', htmlSnippet2, testClient); await updateSnippet('typescript.json', tsSnippet2, testClient); - let preview = await testObject.preview(await testClient.getResourceManifest(), {}); + let preview = await testObject.sync(await testClient.getResourceManifest(), true); - assert.strictEqual(testObject.status, SyncStatus.Syncing); + assert.strictEqual(testObject.status, SyncStatus.HasConflicts); assertPreviews(preview!.resourcePreviews, [ joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), ]); - assert.deepStrictEqual(testObject.conflicts.conflicts, []); + assertPreviews(testObject.conflicts.conflicts, + [ + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json'), + joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'typescript.json'), + ]); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, htmlSnippet2); preview = await testObject.accept(preview!.resourcePreviews[1].previewResource, tsSnippet2); diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts index 4dfec270711..31a55803660 100644 --- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts +++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts @@ -15,7 +15,7 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/c import { IFileService } from '../../../files/common/files.js'; import { IStorageService, StorageScope } from '../../../storage/common/storage.js'; import { IUserDataProfilesService } from '../../../userDataProfile/common/userDataProfile.js'; -import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from '../../common/abstractSynchronizer.js'; +import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview, SyncStrategy } from '../../common/abstractSynchronizer.js'; import { Change, IRemoteUserData, IResourcePreview as IBaseResourcePreview, IUserDataResourceManifest, IUserDataSyncConfiguration, IUserDataSyncStoreService, MergeState, SyncResource, SyncStatus, USER_DATA_SYNC_SCHEME } from '../../common/userDataSync.js'; import { UserDataSyncClient, UserDataSyncTestServer } from './userDataSyncClient.js'; @@ -45,7 +45,7 @@ class TestSynchroniser extends AbstractSynchroniser { return super.getLatestRemoteUserData(manifest, lastSyncUserData); } - protected override async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, apply: boolean, userDataSyncConfiguration: IUserDataSyncConfiguration): Promise { + protected override async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, strategy: SyncStrategy, userDataSyncConfiguration: IUserDataSyncConfiguration): Promise { this.cancelled = false; this.onDoSyncCall.fire(); await this.syncBarrier.wait(); @@ -54,7 +54,7 @@ class TestSynchroniser extends AbstractSynchroniser { return SyncStatus.Idle; } - return super.doSync(remoteUserData, lastSyncUserData, apply, userDataSyncConfiguration); + return super.doSync(remoteUserData, lastSyncUserData, strategy, userDataSyncConfiguration); } protected override async generateSyncPreview(remoteUserData: IRemoteUserData): Promise { @@ -536,22 +536,7 @@ suite('TestSynchronizer - Manual Sync', () => { testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); - const preview = await testObject.preview(await client.getResourceManifest(), {}); - - assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assertConflicts(testObject.conflicts.conflicts, []); - }); - }); - - test('preview -> merge', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + const preview = await testObject.sync(await client.getResourceManifest(), true); assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); assertPreviews(preview!.resourcePreviews, [testObject.localResource]); @@ -566,24 +551,7 @@ suite('TestSynchronizer - Manual Sync', () => { testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); - - assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); - assertConflicts(testObject.conflicts.conflicts, []); - }); - }); - - test('preview -> merge -> accept', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.accept(preview!.resourcePreviews[0].localResource); assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); @@ -601,8 +569,7 @@ suite('TestSynchronizer - Manual Sync', () => { await testObject.sync(await client.getResourceManifest()); const manifest = await client.getResourceManifest(); - let preview = await testObject.preview(manifest, {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(manifest, true); preview = await testObject.apply(false); assert.deepStrictEqual(testObject.status, SyncStatus.Idle); @@ -624,7 +591,7 @@ suite('TestSynchronizer - Manual Sync', () => { const manifest = await client.getResourceManifest(); const expectedContent = manifest![testObject.resource]; - let preview = await testObject.preview(manifest, {}); + let preview = await testObject.sync(manifest, true); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); preview = await testObject.apply(false); @@ -637,36 +604,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('preview -> merge -> accept -> apply', async () => { + test('preivew -> discard', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); - await testObject.sync(await client.getResourceManifest()); - const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); - preview = await testObject.accept(preview!.resourcePreviews[0].localResource); - preview = await testObject.apply(false); - - assert.deepStrictEqual(testObject.status, SyncStatus.Idle); - assert.strictEqual(preview, null); - assertConflicts(testObject.conflicts.conflicts, []); - - assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); - assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); - }); - - test('preivew -> merge -> discard', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); @@ -676,14 +620,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('preivew -> merge -> discard -> accept', async () => { + test('preivew -> discard -> accept', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); @@ -694,30 +637,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('preivew -> accept -> discard', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); - preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); - - assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); - assertConflicts(testObject.conflicts.conflicts, []); - }); - }); - test('preivew -> accept -> discard -> accept', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); @@ -729,32 +655,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('preivew -> accept -> discard -> merge', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); - preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); - preview = await testObject.merge(preview!.resourcePreviews[0].remoteResource); - - assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Accepted); - assertConflicts(testObject.conflicts.conflicts, []); - }); - }); - - test('preivew -> merge -> accept -> discard', async () => { + test('preivew -> accept -> discard', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); @@ -765,29 +672,7 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('preivew -> merge -> discard -> accept -> apply', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - await testObject.sync(await client.getResourceManifest()); - - const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); - preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); - preview = await testObject.accept(preview!.resourcePreviews[0].localResource); - preview = await testObject.apply(false); - - assert.deepStrictEqual(testObject.status, SyncStatus.Idle); - assert.strictEqual(preview, null); - assertConflicts(testObject.conflicts.conflicts, []); - assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); - assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); - }); - - test('preivew -> accept -> discard -> accept -> apply', async () => { + test('preivew -> discard -> accept -> apply', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; @@ -795,8 +680,7 @@ suite('TestSynchronizer - Manual Sync', () => { await testObject.sync(await client.getResourceManifest()); const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].localResource); @@ -810,53 +694,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('preivew -> accept -> discard -> merge -> apply', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - await testObject.sync(await client.getResourceManifest()); - - const manifest = await client.getResourceManifest(); - const expectedContent = manifest![testObject.resource]; - let preview = await testObject.preview(manifest, {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); - preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); - preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); - preview = await testObject.merge(preview!.resourcePreviews[0].localResource); - preview = await testObject.apply(false); - - assert.deepStrictEqual(testObject.status, SyncStatus.Idle); - assert.strictEqual(preview, null); - assertConflicts(testObject.conflicts.conflicts, []); - - assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); - assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); - }); - test('conflicts: preview', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - const preview = await testObject.preview(await client.getResourceManifest(), {}); - - assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assertConflicts(testObject.conflicts.conflicts, []); - }); - }); - - test('conflicts: preview -> merge', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: true, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + const preview = await testObject.sync(await client.getResourceManifest(), true); assert.deepStrictEqual(testObject.status, SyncStatus.HasConflicts); assertPreviews(preview!.resourcePreviews, [testObject.localResource]); @@ -865,14 +709,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('conflicts: preview -> merge -> discard', async () => { + test('conflicts: preview -> discard', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - const preview = await testObject.preview(await client.getResourceManifest(), {}); - await testObject.merge(preview!.resourcePreviews[0].previewResource); + const preview = await testObject.sync(await client.getResourceManifest(), true); await testObject.discard(preview!.resourcePreviews[0].previewResource); assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); @@ -888,8 +731,7 @@ suite('TestSynchronizer - Manual Sync', () => { testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); const content = await testObject.resolveContent(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, content); @@ -899,38 +741,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('conflicts: preview -> merge -> accept -> apply', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: false, hasError: false }; - testObject.syncBarrier.open(); - await testObject.sync(await client.getResourceManifest()); - - testObject.syncResult = { hasConflicts: true, hasError: false }; - const manifest = await client.getResourceManifest(); - const expectedContent = manifest![testObject.resource]; - let preview = await testObject.preview(manifest, {}); - - await testObject.merge(preview!.resourcePreviews[0].previewResource); - preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); - preview = await testObject.apply(false); - - assert.deepStrictEqual(testObject.status, SyncStatus.Idle); - assert.strictEqual(preview, null); - assertConflicts(testObject.conflicts.conflicts, []); - - assert.strictEqual((await testObject.getRemoteUserData(null)).syncData?.content, expectedContent); - assert.strictEqual((await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(), expectedContent); - }); - }); - test('conflicts: preview -> accept 2', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); + let preview = await testObject.sync(await client.getResourceManifest(), true); const content = await testObject.resolveContent(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, content); @@ -950,7 +767,7 @@ suite('TestSynchronizer - Manual Sync', () => { testObject.syncResult = { hasConflicts: true, hasError: false }; const manifest = await client.getResourceManifest(); const expectedContent = manifest![testObject.resource]; - let preview = await testObject.preview(manifest, {}); + let preview = await testObject.sync(manifest, true); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); preview = await testObject.apply(false); @@ -964,14 +781,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('conflicts: preivew -> merge -> discard', async () => { + test('conflicts: preivew -> discard', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); @@ -981,14 +797,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('conflicts: preivew -> merge -> discard -> accept', async () => { + test('conflicts: preivew -> discard -> accept', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); @@ -999,30 +814,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('conflicts: preivew -> accept -> discard', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: true, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); - preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); - - assert.deepStrictEqual(testObject.status, SyncStatus.Syncing); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Preview); - assertConflicts(testObject.conflicts.conflicts, []); - }); - }); - test('conflicts: preivew -> accept -> discard -> accept', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: true, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); @@ -1034,50 +832,13 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('conflicts: preivew -> accept -> discard -> merge', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: true, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.accept(preview!.resourcePreviews[0].previewResource); - preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); - preview = await testObject.merge(preview!.resourcePreviews[0].remoteResource); - - assert.deepStrictEqual(testObject.status, SyncStatus.HasConflicts); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); - assertConflicts(testObject.conflicts.conflicts, [preview!.resourcePreviews[0].localResource]); - }); - }); - - test('conflicts: preivew -> merge -> discard -> merge', async () => { - await runWithFakedTimers({}, async () => { - const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); - testObject.syncResult = { hasConflicts: true, hasError: false }; - testObject.syncBarrier.open(); - - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); - preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); - preview = await testObject.merge(preview!.resourcePreviews[0].remoteResource); - - assert.deepStrictEqual(testObject.status, SyncStatus.HasConflicts); - assertPreviews(preview!.resourcePreviews, [testObject.localResource]); - assert.strictEqual(preview!.resourcePreviews[0].mergeState, MergeState.Conflict); - assertConflicts(testObject.conflicts.conflicts, [preview!.resourcePreviews[0].localResource]); - }); - }); - - test('conflicts: preivew -> merge -> accept -> discard', async () => { + test('conflicts: preivew -> accept -> discard', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; testObject.syncBarrier.open(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); @@ -1088,7 +849,7 @@ suite('TestSynchronizer - Manual Sync', () => { }); }); - test('conflicts: preivew -> merge -> discard -> accept -> apply', async () => { + test('conflicts: preivew -> discard -> accept -> apply', async () => { await runWithFakedTimers({}, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); testObject.syncResult = { hasConflicts: false, hasError: false }; @@ -1096,8 +857,7 @@ suite('TestSynchronizer - Manual Sync', () => { await testObject.sync(await client.getResourceManifest()); const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].localResource); preview = await testObject.apply(false); @@ -1118,8 +878,7 @@ suite('TestSynchronizer - Manual Sync', () => { await testObject.sync(await client.getResourceManifest()); const expectedContent = (await client.instantiationService.get(IFileService).readFile(testObject.localResource)).value.toString(); - let preview = await testObject.preview(await client.getResourceManifest(), {}); - preview = await testObject.merge(preview!.resourcePreviews[0].previewResource); + let preview = await testObject.sync(await client.getResourceManifest(), true); preview = await testObject.accept(preview!.resourcePreviews[0].remoteResource); preview = await testObject.discard(preview!.resourcePreviews[0].previewResource); preview = await testObject.accept(preview!.resourcePreviews[0].localResource); diff --git a/src/vs/platform/userDataSync/test/common/tasksSync.test.ts b/src/vs/platform/userDataSync/test/common/tasksSync.test.ts index 26fa39aeeb2..e875a66455d 100644 --- a/src/vs/platform/userDataSync/test/common/tasksSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/tasksSync.test.ts @@ -540,7 +540,7 @@ suite('TasksSync', () => { await fileService.del(tasksResource); } - const preview = (await testObject.preview(await client.getResourceManifest(), {}))!; + const preview = (await testObject.sync(await client.getResourceManifest(), true))!; server.reset(); const content = await testObject.resolveContent(preview.resourcePreviews[0].remoteResource); diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index d8ed8a95c5f..cb2e6e81555 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -21,7 +21,7 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { IProgress, IProgressService, IProgressStep, ProgressLocation } from '../../../../platform/progress/common/progress.js'; import { EditSessionsWorkbenchService } from './editSessionsStorageService.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { UserDataSyncErrorCode, UserDataSyncStoreError, IUserDataSynchroniser } from '../../../../platform/userDataSync/common/userDataSync.js'; +import { UserDataSyncErrorCode, UserDataSyncStoreError } from '../../../../platform/userDataSync/common/userDataSync.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; import { getFileNamesMessage, IDialogService, IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; @@ -124,7 +124,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo private registeredCommands = new Set(); - private workspaceStateSynchronizer: IUserDataSynchroniser | undefined; + private workspaceStateSynchronizer: WorkspaceStateSynchroniser | undefined; private editSessionsStorageClient: EditSessionsStoreClient | undefined; constructor( @@ -565,7 +565,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } } - await this.workspaceStateSynchronizer?.apply(false, {}); + await this.workspaceStateSynchronizer?.apply(); this.logService.info(`Deleting edit session with ref ${ref} after successfully applying it to current workspace...`); await this.editSessionsStorageService.delete('editSessions', ref); @@ -743,7 +743,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo } // Store contributed workspace state - await this.workspaceStateSynchronizer?.sync(null, {}); + await this.workspaceStateSynchronizer?.sync(); if (!hasEdits) { this.logService.info('Skipped storing working changes in the cloud as there are no edits to store.'); diff --git a/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts b/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts index dd6f6145901..0068c223d2b 100644 --- a/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts +++ b/src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts @@ -16,7 +16,7 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IUserDataProfile } from '../../../../platform/userDataProfile/common/userDataProfile.js'; import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview, ISyncResourcePreview } from '../../../../platform/userDataSync/common/abstractSynchronizer.js'; -import { IRemoteUserData, IResourceRefHandle, IUserDataSyncLocalStoreService, IUserDataSyncConfiguration, IUserDataSyncEnablementService, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSynchroniser, IWorkspaceState, SyncResource } from '../../../../platform/userDataSync/common/userDataSync.js'; +import { IRemoteUserData, IResourceRefHandle, IUserDataSyncLocalStoreService, IUserDataSyncConfiguration, IUserDataSyncEnablementService, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSynchroniser, IWorkspaceState, SyncResource, IUserDataSyncResourcePreview } from '../../../../platform/userDataSync/common/userDataSync.js'; import { EditSession, IEditSessionsStorageService } from './editSessions.js'; import { IWorkspaceIdentityService } from '../../../services/workspaces/common/workspaceIdentityService.js'; @@ -75,11 +75,11 @@ export class WorkspaceStateSynchroniser extends AbstractSynchroniser implements super({ syncResource: SyncResource.WorkspaceState, profile }, collection, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncLocalStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService, uriIdentityService); } - override async sync(): Promise { + override async sync(): Promise { const cancellationTokenSource = new CancellationTokenSource(); const folders = await this.workspaceIdentityService.getWorkspaceStateFolders(cancellationTokenSource.token); if (!folders.length) { - return; + return null; } // Ensure we have latest state by sending out onWillSaveState event @@ -87,7 +87,7 @@ export class WorkspaceStateSynchroniser extends AbstractSynchroniser implements const keys = this.storageService.keys(StorageScope.WORKSPACE, StorageTarget.USER); if (!keys.length) { - return; + return null; } const contributedData: IStringDictionary = {}; @@ -100,6 +100,7 @@ export class WorkspaceStateSynchroniser extends AbstractSynchroniser implements const content: IWorkspaceState = { folders, storage: contributedData, version: this.version }; await this.editSessionsStorageService.write('workspaceState', stringify(content)); + return null; } override async apply(): Promise { From 3a4f4164c7ec15e5607c47c7666c4bde99a21958 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:49:37 +0100 Subject: [PATCH 355/479] Git - fix context key for unstageSelectedRanges (#236476) --- extensions/git/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 08349fd4783..d63027619d1 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1054,7 +1054,7 @@ }, { "command": "git.unstageSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && resourceScheme == git" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == git" }, { "command": "git.clean", From a02cb4218adce96301f7561d7ce585cdeb84598f Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 18 Dec 2024 09:10:16 -0600 Subject: [PATCH 356/479] add `when` for terminal chat keybinding (#236480) fix #236474 --- .../terminalContrib/chat/browser/terminalChatActions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts index deeb48f48c7..0d6fdd35361 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions.ts @@ -224,7 +224,8 @@ registerActiveXtermAction({ ), keybinding: { weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyCode.KeyR + primary: KeyMod.CtrlCmd | KeyCode.KeyR, + when: TerminalChatContextKeys.focused }, menu: { id: MENU_TERMINAL_CHAT_WIDGET_STATUS, From cec5112d1172e69cee13d729f5bc21ceaad36519 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 18 Dec 2024 16:40:44 +0100 Subject: [PATCH 357/479] Fixes https://github.com/microsoft/vscode-copilot/issues/9818 (#236486) --- .../browser/controller/inlineCompletionsController.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index f3720436c2f..7c7e4495719 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -181,14 +181,16 @@ export class InlineCompletionsController extends Disposable { return; } - if (this.model.get()?.inlineEditAvailable.get()) { - // dont hide inline edits on blur + const model = this.model.get(); + if (!model) { return; } + if (model.state.get()?.inlineCompletion?.request.isExplicitRequest && model.inlineEditAvailable.get()) { + // dont hide inline edits on blur when requested explicitly return; } transaction(tx => { /** @description InlineCompletionsController.onDidBlurEditorWidget */ - this.model.get()?.stop('automatic', tx); + model.stop('automatic', tx); }); })); From 929ce7c1fa6bd6acccf703b5b1c491659b63ef11 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 18 Dec 2024 16:45:47 +0100 Subject: [PATCH 358/479] fixes a random bunch of leaking disposables (#236487) Adds (disabled for now) disposable tracker that is based on the finalization registry, gist is that every GC'ed disposable that isn't disposed is a bug. --- .../ui/breadcrumbs/breadcrumbsWidget.ts | 1 + src/vs/base/common/async.ts | 2 +- src/vs/base/common/lifecycle.ts | 28 +++++++++++++++++++ .../services/hoverService/hoverService.ts | 2 +- .../codeAction/browser/codeActionModel.ts | 1 + .../chat/browser/actions/chatActions.ts | 4 +-- .../debug/common/debugContentProvider.ts | 9 ++++-- .../browser/performance.contribution.ts | 25 ++++++++++++++++- .../browser/searchTreeModel/fileMatch.ts | 12 ++++---- .../common/notificationService.ts | 2 +- .../progress/browser/progressService.ts | 5 +++- 11 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index b87e883bd3a..a4cb3361f9d 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -282,6 +282,7 @@ export class BreadcrumbsWidget { removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix)); this._render(prefix); dispose(removed); + dispose(items.slice(0, prefix)); this._focus(-1, undefined); } catch (e) { const newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`); diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index bd76ba77e4a..84feeb77704 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -557,7 +557,7 @@ export function disposableTimeout(handler: () => void, timeout = 0, store?: Disp }, timeout); const disposable = toDisposable(() => { clearTimeout(timer); - store?.deleteAndLeak(disposable); + store?.delete(disposable); }); store?.add(disposable); return disposable; diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 4cc2d172bf4..e04e62f76d3 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -44,6 +44,34 @@ export interface IDisposableTracker { markAsSingleton(disposable: IDisposable): void; } +export class GCBasedDisposableTracker implements IDisposableTracker { + + private readonly _registry = new FinalizationRegistry(heldValue => { + console.warn(`[LEAKED DISPOSABLE] ${heldValue}`); + }); + + trackDisposable(disposable: IDisposable): void { + const stack = new Error('CREATED via:').stack!; + this._registry.register(disposable, stack, disposable); + } + + setParent(child: IDisposable, parent: IDisposable | null): void { + if (parent) { + this._registry.unregister(child); + } else { + this.trackDisposable(child); + } + } + + markAsDisposed(disposable: IDisposable): void { + this._registry.unregister(disposable); + } + + markAsSingleton(disposable: IDisposable): void { + this._registry.unregister(disposable); + } +} + export interface DisposableInfo { value: IDisposable; source: string | null; diff --git a/src/vs/editor/browser/services/hoverService/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts index b4164c04d1d..873ece14a4f 100644 --- a/src/vs/editor/browser/services/hoverService/hoverService.ts +++ b/src/vs/editor/browser/services/hoverService/hoverService.ts @@ -51,7 +51,7 @@ export class HoverService extends Disposable implements IHoverService { ) { super(); - contextMenuService.onDidShowContextMenu(() => this.hideHover()); + this._register(contextMenuService.onDidShowContextMenu(() => this.hideHover())); this._contextViewHandler = this._register(new ContextViewHandler(this._layoutService)); } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index a909d559ed8..10381ff4ffc 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -235,6 +235,7 @@ export class CodeActionModel extends Disposable { const codeActionSet = await getCodeActions(this._registry, model, trigger.selection, trigger.trigger, Progress.None, token); const allCodeActions = [...codeActionSet.allActions]; if (token.isCancellationRequested) { + codeActionSet.dispose(); return emptyCodeActionSet; } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 4cb15f40d68..a0bb888eb80 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -561,7 +561,7 @@ export class ChatCommandCenterRendering extends Disposable implements IWorkbench const contextKeySet = new Set([ChatContextKeys.Setup.signedOut.key]); - actionViewItemService.register(MenuId.CommandCenter, MenuId.ChatCommandCenter, (action, options) => { + this._store.add(actionViewItemService.register(MenuId.CommandCenter, MenuId.ChatCommandCenter, (action, options) => { if (!(action instanceof SubmenuItemAction)) { return undefined; } @@ -607,6 +607,6 @@ export class ChatCommandCenterRendering extends Disposable implements IWorkbench agentService.onDidChangeAgents, chatQuotasService.onDidChangeQuotas, Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(contextKeySet)) - )); + ))); } } diff --git a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts index 935d2c3db2d..5ad05e1cb7d 100644 --- a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts +++ b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts @@ -19,6 +19,7 @@ import { Range } from '../../../../editor/common/core/range.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; import { ErrorNoTelemetry } from '../../../../base/common/errors.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; /** * Debug URI format @@ -33,7 +34,7 @@ import { ErrorNoTelemetry } from '../../../../base/common/errors.js'; * the arbitrary_path and the session id are encoded with 'encodeURIComponent' * */ -export class DebugContentProvider implements IWorkbenchContribution, ITextModelContentProvider { +export class DebugContentProvider extends Disposable implements IWorkbenchContribution, ITextModelContentProvider { private static INSTANCE: DebugContentProvider; @@ -46,12 +47,14 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC @ILanguageService private readonly languageService: ILanguageService, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService ) { - textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this); + super(); + this._store.add(textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this)); DebugContentProvider.INSTANCE = this; } - dispose(): void { + override dispose(): void { this.pendingUpdates.forEach(cancellationSource => cancellationSource.dispose()); + super.dispose(); } provideTextContent(resource: uri): Promise | null { diff --git a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts index a4b1a39c3e4..bd69320c358 100644 --- a/src/vs/workbench/contrib/performance/browser/performance.contribution.ts +++ b/src/vs/workbench/contrib/performance/browser/performance.contribution.ts @@ -9,13 +9,15 @@ import { IInstantiationService, ServicesAccessor } from '../../../../platform/in import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { Extensions, IWorkbenchContributionsRegistry, registerWorkbenchContribution2 } from '../../../common/contributions.js'; +import { Extensions, IWorkbenchContributionsRegistry, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; import { EditorExtensions, IEditorSerializer, IEditorFactoryRegistry } from '../../../common/editor.js'; import { PerfviewContrib, PerfviewInput } from './perfviewEditor.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { InstantiationService, Trace } from '../../../../platform/instantiation/common/instantiationService.js'; import { EventProfiling } from '../../../../base/common/event.js'; import { InputLatencyContrib } from './inputLatencyContrib.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { GCBasedDisposableTracker, setDisposableTracker } from '../../../../base/common/lifecycle.js'; // -- startup performance view @@ -136,3 +138,24 @@ Registry.as(Extensions.Workbench).registerWorkb InputLatencyContrib, LifecyclePhase.Eventually ); + + +// -- track leaking disposables, those that get GC'ed before having been disposed + +// this is currently disabled because there is too many leaks and some false positives, e.g disposables from registers +// like MenuRegistry, CommandsRegistery etc should be marked as singleton + +const _enableLeakDetection = false + // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this + ; + +class DisposableTracking { + static readonly Id = 'perf.disposableTracking'; + constructor(@IEnvironmentService envService: IEnvironmentService) { + if (!envService.isBuilt && _enableLeakDetection) { + setDisposableTracker(new GCBasedDisposableTracker()); + } + } +} + +registerWorkbenchContribution2(DisposableTracking.Id, DisposableTracking, WorkbenchPhase.Eventually); diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts index 1b5d2fa717b..dfaf589263b 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts @@ -5,7 +5,7 @@ import { RunOnceScheduler } from '../../../../../base/common/async.js'; import { Lazy } from '../../../../../base/common/lazy.js'; -import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { themeColorFromId } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { TrackedRangeStickiness, MinimapPosition, ITextModel, FindMatch, IModelDeltaDecoration } from '../../../../../editor/common/model.js'; @@ -69,7 +69,7 @@ export class FileMatchImpl extends Disposable implements ISearchTreeFileMatch { protected _resource: URI; private _fileStat?: IFileStatWithPartialMetadata; private _model: ITextModel | null = null; - private _modelListener: IDisposable | null = null; + private _modelListener: DisposableStore | null = null; protected _textMatches: Map; private _removedTextMatches: Set; @@ -132,10 +132,11 @@ export class FileMatchImpl extends Disposable implements ISearchTreeFileMatch { } bindModel(model: ITextModel): void { this._model = model; - this._modelListener = this._model.onDidChangeContent(() => { + this._modelListener = new DisposableStore(); + this._modelListener.add(this._model.onDidChangeContent(() => { this._updateScheduler.schedule(); - }); - this._model.onWillDispose(() => this.onModelWillDispose()); + })); + this._modelListener.add(this._model.onWillDispose(() => this.onModelWillDispose())); this.updateHighlights(); } @@ -360,4 +361,3 @@ export class FileMatchImpl extends Disposable implements ISearchTreeFileMatch { //#endregion } - diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 151edfccf9f..f557bc098c1 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -272,7 +272,6 @@ export class NotificationService extends Disposable implements INotificationServ } prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { - const toDispose = new DisposableStore(); // Handle neverShowAgain option accordingly if (options?.neverShowAgain) { @@ -300,6 +299,7 @@ export class NotificationService extends Disposable implements INotificationServ } let choiceClicked = false; + const toDispose = new DisposableStore(); // Convert choices into primary/secondary actions diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 872300d3937..73b4c71daba 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -356,7 +356,10 @@ export class ProgressService extends Disposable implements IProgressService { } // Clear upon dispose - Event.once(notification.onDidClose)(() => notificationDisposables.dispose()); + Event.once(notification.onDidClose)(() => { + notificationDisposables.dispose(); + dispose(windowProgressDisposable); + }); return notification; }; From 148a1ca4bd890aa4743327988070da0546180dd2 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 18 Dec 2024 17:41:28 +0100 Subject: [PATCH 359/479] TreeView: MaxCallStackError - Nesting (#236493) Part of #233056 --- src/vs/base/browser/ui/list/listView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index e149029df4f..00a19352e91 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -9,7 +9,7 @@ import { DomEmitter } from '../../event.js'; import { IMouseWheelEvent } from '../../mouseEvent.js'; import { EventType as TouchEventType, Gesture, GestureEvent } from '../../touch.js'; import { SmoothScrollableElement } from '../scrollbar/scrollableElement.js'; -import { distinct, equals } from '../../../common/arrays.js'; +import { distinct, equals, splice } from '../../../common/arrays.js'; import { Delayer, disposableTimeout } from '../../../common/async.js'; import { memoize } from '../../../common/decorators.js'; import { Emitter, Event, IValueWithChangeEvent } from '../../../common/event.js'; @@ -643,7 +643,7 @@ export class ListView implements IListView { this.items = inserted; } else { this.rangeMap.splice(start, deleteCount, inserted); - deleted = this.items.splice(start, deleteCount, ...inserted); + deleted = splice(this.items, start, deleteCount, inserted); } const delta = elements.length - deleteCount; From 7d0efabd3b29e465be0c76c50ffedec391de3379 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 18 Dec 2024 17:47:19 +0100 Subject: [PATCH 360/479] Adds single observable logging (#236481) * Adds single observable logging * Improved observePosition * Fixes CI * Fixes tests --- src/vs/base/common/observableInternal/base.ts | 13 +++- .../base/common/observableInternal/derived.ts | 10 +++ .../observableInternal/lazyObservableValue.ts | 7 +- .../base/common/observableInternal/logging.ts | 69 +++++++++++++++---- src/vs/base/test/common/observable.test.ts | 10 +-- src/vs/editor/browser/observableCodeEditor.ts | 6 +- 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/vs/base/common/observableInternal/base.ts b/src/vs/base/common/observableInternal/base.ts index 3ce43ead2ae..c28e773b268 100644 --- a/src/vs/base/common/observableInternal/base.ts +++ b/src/vs/base/common/observableInternal/base.ts @@ -6,7 +6,7 @@ import { DebugNameData, DebugOwner, getFunctionName } from './debugName.js'; import { DisposableStore, EqualityComparer, IDisposable, strictEquals } from './commonFacade/deps.js'; import type { derivedOpts } from './derived.js'; -import { getLogger } from './logging.js'; +import { getLogger, logObservable } from './logging.js'; import { keepObserved, recomputeInitiallyAndOnChange } from './utils.js'; /** @@ -67,6 +67,12 @@ export interface IObservable { flatten(this: IObservable>): IObservable; + /** + * ONLY FOR DEBUGGING! + * Logs computations of this derived. + */ + log(): IObservable; + /** * Makes sure this value is computed eagerly. */ @@ -233,6 +239,11 @@ export abstract class ConvenientObservable implements IObservable { + logObservable(this); + return this; + } + /** * @sealed * Converts an observable of an observable value into a direct observable of the value. diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index 80cc9a72124..ba018041799 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -459,6 +459,16 @@ export class Derived extends BaseObservable im } super.removeObserver(observer); } + + public override log(): IObservable { + if (!getLogger()) { + super.log(); + getLogger()?.handleDerivedCreated(this); + } else { + super.log(); + } + return this; + } } diff --git a/src/vs/base/common/observableInternal/lazyObservableValue.ts b/src/vs/base/common/observableInternal/lazyObservableValue.ts index 8a3f63c05d7..6c0f85aa876 100644 --- a/src/vs/base/common/observableInternal/lazyObservableValue.ts +++ b/src/vs/base/common/observableInternal/lazyObservableValue.ts @@ -6,6 +6,7 @@ import { EqualityComparer } from './commonFacade/deps.js'; import { BaseObservable, IObserver, ISettableObservable, ITransaction, TransactionImpl } from './base.js'; import { DebugNameData } from './debugName.js'; +import { getLogger } from './logging.js'; /** * Holds off updating observers until the value is actually read. @@ -42,13 +43,15 @@ export class LazyObservableValue this._isUpToDate = true; if (this._deltas.length > 0) { - for (const observer of this.observers) { - for (const change of this._deltas) { + for (const change of this._deltas) { + getLogger()?.handleObservableChanged(this, { change, didChange: true, oldValue: '(unknown)', newValue: this._value, hadValue: true }); + for (const observer of this.observers) { observer.handleChange(this, change); } } this._deltas.length = 0; } else { + getLogger()?.handleObservableChanged(this, { change: undefined, didChange: true, oldValue: '(unknown)', newValue: this._value, hadValue: true }); for (const observer of this.observers) { observer.handleChange(this, undefined); } diff --git a/src/vs/base/common/observableInternal/logging.ts b/src/vs/base/common/observableInternal/logging.ts index 0de37a858cb..0c343b548e4 100644 --- a/src/vs/base/common/observableInternal/logging.ts +++ b/src/vs/base/common/observableInternal/logging.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { AutorunObserver } from './autorun.js'; -import { IObservable, ObservableValue, TransactionImpl } from './base.js'; +import { IObservable, TransactionImpl } from './base.js'; import { Derived } from './derived.js'; import { FromEventObservable } from './utils.js'; @@ -18,6 +18,18 @@ export function getLogger(): IObservableLogger | undefined { return globalObservableLogger; } +export function logObservable(obs: IObservable): void { + if (!globalObservableLogger) { + const l = new ConsoleObservableLogger(); + l.addFilteredObj(obs); + setLogger(l); + } else { + if (globalObservableLogger instanceof ConsoleObservableLogger) { + (globalObservableLogger as ConsoleObservableLogger).addFilteredObj(obs); + } + } +} + interface IChangeInformation { oldValue: unknown; newValue: unknown; @@ -27,7 +39,7 @@ interface IChangeInformation { } export interface IObservableLogger { - handleObservableChanged(observable: ObservableValue, info: IChangeInformation): void; + handleObservableChanged(observable: IObservable, info: IChangeInformation): void; handleFromEventObservableTriggered(observable: FromEventObservable, info: IChangeInformation): void; handleAutorunCreated(autorun: AutorunObserver): void; @@ -44,6 +56,19 @@ export interface IObservableLogger { export class ConsoleObservableLogger implements IObservableLogger { private indentation = 0; + private _filteredObjects: Set | undefined; + + public addFilteredObj(obj: unknown): void { + if (!this._filteredObjects) { + this._filteredObjects = new Set(); + } + this._filteredObjects.add(obj); + } + + private _isIncluded(obj: unknown): boolean { + return this._filteredObjects?.has(obj) ?? true; + } + private textToConsoleArgs(text: ConsoleText): unknown[] { return consoleTextToArgs([ normalText(repeat('| ', this.indentation)), @@ -77,6 +102,7 @@ export class ConsoleObservableLogger implements IObservableLogger { } handleObservableChanged(observable: IObservable, info: IChangeInformation): void { + if (!this._isIncluded(observable)) { return; } console.log(...this.textToConsoleArgs([ formatKind('observable value changed'), styled(observable.debugName, { color: 'BlueViolet' }), @@ -130,7 +156,10 @@ export class ConsoleObservableLogger implements IObservableLogger { } handleDerivedRecomputed(derived: Derived, info: IChangeInformation): void { - const changedObservables = this.changedObservablesSets.get(derived)!; + if (!this._isIncluded(derived)) { return; } + + const changedObservables = this.changedObservablesSets.get(derived); + if (!changedObservables) { return; } console.log(...this.textToConsoleArgs([ formatKind('derived recomputed'), styled(derived.debugName, { color: 'BlueViolet' }), @@ -142,6 +171,8 @@ export class ConsoleObservableLogger implements IObservableLogger { } handleFromEventObservableTriggered(observable: FromEventObservable, info: IChangeInformation): void { + if (!this._isIncluded(observable)) { return; } + console.log(...this.textToConsoleArgs([ formatKind('observable from event triggered'), styled(observable.debugName, { color: 'BlueViolet' }), @@ -151,6 +182,8 @@ export class ConsoleObservableLogger implements IObservableLogger { } handleAutorunCreated(autorun: AutorunObserver): void { + if (!this._isIncluded(autorun)) { return; } + const existingHandleChange = autorun.handleChange; this.changedObservablesSets.set(autorun, new Set()); autorun.handleChange = (observable, change) => { @@ -160,13 +193,17 @@ export class ConsoleObservableLogger implements IObservableLogger { } handleAutorunTriggered(autorun: AutorunObserver): void { - const changedObservables = this.changedObservablesSets.get(autorun)!; - console.log(...this.textToConsoleArgs([ - formatKind('autorun'), - styled(autorun.debugName, { color: 'BlueViolet' }), - this.formatChanges(changedObservables), - { data: [{ fn: autorun._debugNameData.referenceFn ?? autorun._runFn }] } - ])); + const changedObservables = this.changedObservablesSets.get(autorun); + if (!changedObservables) { return; } + + if (this._isIncluded(autorun)) { + console.log(...this.textToConsoleArgs([ + formatKind('autorun'), + styled(autorun.debugName, { color: 'BlueViolet' }), + this.formatChanges(changedObservables), + { data: [{ fn: autorun._debugNameData.referenceFn ?? autorun._runFn }] } + ])); + } changedObservables.clear(); this.indentation++; } @@ -180,11 +217,13 @@ export class ConsoleObservableLogger implements IObservableLogger { if (transactionName === undefined) { transactionName = ''; } - console.log(...this.textToConsoleArgs([ - formatKind('transaction'), - styled(transactionName, { color: 'BlueViolet' }), - { data: [{ fn: transaction._fn }] } - ])); + if (this._isIncluded(transaction)) { + console.log(...this.textToConsoleArgs([ + formatKind('transaction'), + styled(transactionName, { color: 'BlueViolet' }), + { data: [{ fn: transaction._fn }] } + ])); + } this.indentation++; } diff --git a/src/vs/base/test/common/observable.test.ts b/src/vs/base/test/common/observable.test.ts index 2f897924323..3d7ed472fcd 100644 --- a/src/vs/base/test/common/observable.test.ts +++ b/src/vs/base/test/common/observable.test.ts @@ -1507,21 +1507,21 @@ export class LoggingObservableValue implements ISettableObservable { private value: T; - constructor(public readonly debugName: string, initialValue: T, private readonly log: Log) { + constructor(public readonly debugName: string, initialValue: T, private readonly logger: Log) { super(); this.value = initialValue; } protected override onFirstObserverAdded(): void { - this.log.log(`${this.debugName}.firstObserverAdded`); + this.logger.log(`${this.debugName}.firstObserverAdded`); } protected override onLastObserverRemoved(): void { - this.log.log(`${this.debugName}.lastObserverRemoved`); + this.logger.log(`${this.debugName}.lastObserverRemoved`); } public get(): T { - this.log.log(`${this.debugName}.get`); + this.logger.log(`${this.debugName}.get`); return this.value; } @@ -1537,7 +1537,7 @@ export class LoggingObservableValue return; } - this.log.log(`${this.debugName}.set (value ${value})`); + this.logger.log(`${this.debugName}.set (value ${value})`); this.value = value; diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index 404f10dbe46..781a9895212 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -266,13 +266,13 @@ export class ObservableCodeEditor extends Disposable { } public observePosition(position: IObservable, store: DisposableStore): IObservable { - const result = observableValueOpts({ owner: this, equalsFn: equalsIfDefined(Point.equals) }, new Point(0, 0)); + let pos = position.get(); + const result = observableValueOpts({ owner: this, debugName: () => `topLeftOfPosition${pos?.toString()}`, equalsFn: equalsIfDefined(Point.equals) }, new Point(0, 0)); const contentWidgetId = `observablePositionWidget` + (this._widgetCounter++); const domNode = document.createElement('div'); const w: IContentWidget = { getDomNode: () => domNode, getPosition: () => { - const pos = position.get(); return pos ? { preference: [ContentWidgetPositionPreference.EXACT], position: position.get() } : null; }, getId: () => contentWidgetId, @@ -283,7 +283,7 @@ export class ObservableCodeEditor extends Disposable { }; this.editor.addContentWidget(w); store.add(autorun(reader => { - position.read(reader); + pos = position.read(reader); this.editor.layoutContentWidget(w); })); store.add(toDisposable(() => { From aaa5982ec9d269d9c63d0a5e762c7c307b8e5033 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 18 Dec 2024 09:50:52 -0800 Subject: [PATCH 361/479] debug: fix underflow/overflow in breakpoint edit widget (#236428) Fixes #233819 --- src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts | 1 + .../workbench/contrib/debug/browser/media/breakpointWidget.css | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts b/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts index a6a120b040a..d40ff7189c5 100644 --- a/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts @@ -180,6 +180,7 @@ export class GlyphHoverWidget extends Disposable implements IOverlayWidget, IHov const left = editorLayout.glyphMarginLeft + editorLayout.glyphMarginWidth + (laneOrLine === 'lineNo' ? editorLayout.lineNumbersWidth : 0); this._hover.containerDomNode.style.left = `${left}px`; this._hover.containerDomNode.style.top = `${Math.max(Math.round(top), 0)}px`; + this._hover.containerDomNode.style.zIndex = '11'; // 1 more than the zone widget at 10 (#233819) } private _onMouseLeave(e: MouseEvent): void { diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpointWidget.css b/src/vs/workbench/contrib/debug/browser/media/breakpointWidget.css index 4bbf07f03c7..27b11b0a392 100644 --- a/src/vs/workbench/contrib/debug/browser/media/breakpointWidget.css +++ b/src/vs/workbench/contrib/debug/browser/media/breakpointWidget.css @@ -6,6 +6,7 @@ .monaco-editor .zone-widget .zone-widget-container.breakpoint-widget { display: flex; border-color: #007ACC; + background: var(--vscode-editor-background); .breakpoint-select-container { display: flex; From 4fcae8834d70e4beadbeceeedd62d1e12484fe3e Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 18 Dec 2024 12:38:58 -0600 Subject: [PATCH 362/479] do not show no suggestions widget unless it was explicitly invoked (#236505) --- .../browser/terminal.suggest.contribution.ts | 2 +- .../suggest/browser/terminalSuggestAddon.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts index 82b9dc7d514..96676a5a12c 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts @@ -192,7 +192,7 @@ registerActiveInstanceAction({ weight: KeybindingWeight.WorkbenchContrib + 1, when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.terminalShellIntegrationEnabled, ContextKeyExpr.equals(`config.${TerminalSuggestSettingId.Enabled}`, true)) }, - run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.requestCompletions() + run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.requestCompletions(true) }); registerActiveInstanceAction({ diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index 331d36a2e58..9f173cfc15a 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -129,7 +129,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest })); } - private async _handleCompletionProviders(terminal: Terminal | undefined, token: CancellationToken, triggerCharacter?: boolean): Promise { + private async _handleCompletionProviders(terminal: Terminal | undefined, token: CancellationToken, explicitlyInvoked?: boolean): Promise { // Nothing to handle if the terminal is not attached if (!terminal?.element || !this._enableWidget || !this._promptInputModel) { return; @@ -156,7 +156,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest await this._extensionService.activateByEvent('onTerminalCompletionsRequested'); } - const providedCompletions = await this._terminalCompletionService.provideCompletions(this._promptInputModel.prefix, this._promptInputModel.cursorIndex, this._shellType, token, triggerCharacter, doNotRequestExtensionCompletions); + const providedCompletions = await this._terminalCompletionService.provideCompletions(this._promptInputModel.prefix, this._promptInputModel.cursorIndex, this._shellType, token, doNotRequestExtensionCompletions); if (!providedCompletions?.length || token.isCancellationRequested) { return; } @@ -220,7 +220,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest if (token.isCancellationRequested) { return; } - this._showCompletions(model); + this._showCompletions(model, explicitlyInvoked); } setContainerWithOverflow(container: HTMLElement): void { @@ -231,7 +231,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._screen = screen; } - async requestCompletions(triggerCharacter?: boolean): Promise { + async requestCompletions(explicitlyInvoked?: boolean): Promise { if (!this._promptInputModel) { return; } @@ -245,7 +245,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest } this._cancellationTokenSource = new CancellationTokenSource(); const token = this._cancellationTokenSource.token; - await this._handleCompletionProviders(this._terminal, token, triggerCharacter); + await this._handleCompletionProviders(this._terminal, token, explicitlyInvoked); } private _sync(promptInputState: IPromptInputModelState): void { @@ -292,7 +292,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest } for (const char of provider.triggerCharacters) { if (prefix?.endsWith(char)) { - this.requestCompletions(true); + this.requestCompletions(); sent = true; break; } @@ -359,13 +359,13 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest }; } - private _showCompletions(model: SimpleCompletionModel): void { + private _showCompletions(model: SimpleCompletionModel, explicitlyInvoked?: boolean): void { if (!this._terminal?.element) { return; } const suggestWidget = this._ensureSuggestWidget(this._terminal); suggestWidget.setCompletionModel(model); - if (!this._promptInputModel) { + if (!this._promptInputModel || !explicitlyInvoked && model.items.length === 0) { return; } this._model = model; From 4c3f5de78914fd38b4e3c3d46ccd7f777e233c39 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 18 Dec 2024 10:40:34 -0800 Subject: [PATCH 363/479] debug: make ctrl+c copy value(s) in debug views (#236501) Not sure how this ever worked, but it was reported as a bug, and this makes it work. Fixes #232767 --- .../debug/browser/debug.contribution.ts | 12 +++++++ .../contrib/debug/browser/variablesView.ts | 35 ++++++++++++++++--- .../debug/browser/watchExpressionsView.ts | 8 +++-- .../workbench/contrib/debug/common/debug.ts | 5 +++ 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 7c501db354f..6033b0cf9b5 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -63,6 +63,8 @@ import { ReplAccessibilityHelp } from './replAccessibilityHelp.js'; import { ReplAccessibilityAnnouncer } from '../common/replAccessibilityAnnouncer.js'; import { RunAndDebugAccessibilityHelp } from './runAndDebugAccessibilityHelp.js'; import { DebugWatchAccessibilityAnnouncer } from '../common/debugAccessibilityAnnouncer.js'; +import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { FocusedViewContext } from '../../../common/contextkeys.js'; const debugCategory = nls.localize('debugCategory', "Debug"); registerColors(); @@ -206,6 +208,16 @@ registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_WATCH_EXPRESSIONS_COM registerDebugViewMenuItem(MenuId.NotebookVariablesContext, COPY_NOTEBOOK_VARIABLE_VALUE_ID, COPY_NOTEBOOK_VARIABLE_VALUE_LABEL, 20, CONTEXT_VARIABLE_VALUE); +KeybindingsRegistry.registerKeybindingRule({ + id: COPY_VALUE_ID, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.or( + FocusedViewContext.isEqualTo(WATCH_VIEW_ID), + FocusedViewContext.isEqualTo(VARIABLES_VIEW_ID), + ), + primary: KeyMod.CtrlCmd | KeyCode.KeyC +}); + // Touch Bar if (isMacintosh) { diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 222e0a7b57b..16d53c4f87e 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -40,8 +40,9 @@ import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.j import { IViewDescriptorService } from '../../../common/views.js'; import { IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; +import { IViewsService } from '../../../services/views/common/viewsService.js'; import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; -import { CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_VARIABLES_FOCUSED, DataBreakpointSetType, DebugVisualizationType, IDataBreakpointInfoResponse, IDebugService, IExpression, IScope, IStackFrame, IViewModel, VARIABLES_VIEW_ID } from '../common/debug.js'; +import { CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_VARIABLES_FOCUSED, DataBreakpointSetType, DebugVisualizationType, IDataBreakpointInfoResponse, IDebugService, IDebugViewWithVariables, IExpression, IScope, IStackFrame, IViewModel, VARIABLES_VIEW_ID, WATCH_VIEW_ID } from '../common/debug.js'; import { getContextForVariable } from '../common/debugContext.js'; import { ErrorScope, Expression, Scope, StackFrame, Variable, VisualizedExpression, getUriForDebugMemory } from '../common/debugModel.js'; import { DebugVisualizer, IDebugVisualizerService } from '../common/debugVisualizers.js'; @@ -61,7 +62,7 @@ interface IVariablesContext { variable: DebugProtocol.Variable; } -export class VariablesView extends ViewPane { +export class VariablesView extends ViewPane implements IDebugViewWithVariables { private updateTreeScheduler: RunOnceScheduler; private needsRefresh = false; @@ -69,6 +70,10 @@ export class VariablesView extends ViewPane { private savedViewState = new Map(); private autoExpandedScopes = new Set(); + public get treeSelection() { + return this.tree.getSelection(); + } + constructor( options: IViewletViewOptions, @IContextMenuService contextMenuService: IContextMenuService, @@ -653,12 +658,34 @@ CommandsRegistry.registerCommand({ description: COPY_VALUE_LABEL, }, id: COPY_VALUE_ID, - handler: async (accessor: ServicesAccessor, arg: Variable | Expression | IVariablesContext, ctx?: (Variable | Expression)[]) => { + handler: async (accessor: ServicesAccessor, arg: Variable | Expression | IVariablesContext | undefined, ctx?: (Variable | Expression)[]) => { + if (!arg) { + const viewService = accessor.get(IViewsService); + const view = viewService.getActiveViewWithId(WATCH_VIEW_ID) || viewService.getActiveViewWithId(VARIABLES_VIEW_ID); + if (view) { + + } + } const debugService = accessor.get(IDebugService); const clipboardService = accessor.get(IClipboardService); let elementContext = ''; let elements: (Variable | Expression)[]; - if (arg instanceof Variable || arg instanceof Expression) { + if (!arg) { + const viewService = accessor.get(IViewsService); + const focusedView = viewService.getFocusedView(); + let view: IDebugViewWithVariables | null | undefined; + if (focusedView?.id === WATCH_VIEW_ID) { + view = viewService.getActiveViewWithId(WATCH_VIEW_ID); + elementContext = 'watch'; + } else if (focusedView?.id === VARIABLES_VIEW_ID) { + view = viewService.getActiveViewWithId(VARIABLES_VIEW_ID); + elementContext = 'variables'; + } + if (!view) { + return; + } + elements = view.treeSelection.filter(e => e instanceof Expression || e instanceof Variable); + } else if (arg instanceof Variable || arg instanceof Expression) { elementContext = 'watch'; elements = ctx ? ctx : []; } else { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index b1b75a73bc0..b325c138b0a 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -29,7 +29,7 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js'; import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js'; import { IViewDescriptorService } from '../../../common/views.js'; -import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IExpression, WATCH_VIEW_ID } from '../common/debug.js'; +import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IDebugViewWithVariables, IExpression, WATCH_VIEW_ID } from '../common/debug.js'; import { Expression, Variable, VisualizedExpression } from '../common/debugModel.js'; import { AbstractExpressionDataSource, AbstractExpressionsRenderer, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js'; import { DebugExpressionRenderer } from './debugExpressionRenderer.js'; @@ -40,7 +40,7 @@ const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; let ignoreViewUpdates = false; let useCachedEvaluation = false; -export class WatchExpressionsView extends ViewPane { +export class WatchExpressionsView extends ViewPane implements IDebugViewWithVariables { private watchExpressionsUpdatedScheduler: RunOnceScheduler; private needsRefresh = false; @@ -51,6 +51,10 @@ export class WatchExpressionsView extends ViewPane { private menu: IMenu; private expressionRenderer: DebugExpressionRenderer; + public get treeSelection() { + return this.tree.getSelection(); + } + constructor( options: IViewletViewOptions, @IContextMenuService contextMenuService: IContextMenuService, diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index d5387b10408..c0501554472 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -29,6 +29,7 @@ import { Source } from './debugSource.js'; import { ITaskIdentifier } from '../../tasks/common/tasks.js'; import { LiveTestResult } from '../../testing/common/testResult.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { IView } from '../../../common/views.js'; export const VIEWLET_ID = 'workbench.view.debug'; @@ -115,6 +116,10 @@ export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = { description: nls.localize('internalConsoleOptions', "Controls when the internal Debug Console should open.") }; +export interface IDebugViewWithVariables extends IView { + readonly treeSelection: IExpression[]; +} + // raw export interface IRawModelUpdate { From d6d5ebe048e65abd1377b76bdb21935c9bd5d283 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Wed, 18 Dec 2024 10:49:25 -0800 Subject: [PATCH 364/479] Splitting cells should maintain the current edit state (#236507) * split cell command should be in editing mode * stay in preview if splitting from that state * writing the PR description made me realize that this was way simpler --- .../notebook/browser/contrib/cellCommands/cellCommands.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts index 261c8021562..2abc28493e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts @@ -205,6 +205,8 @@ registerAction2(class extends NotebookCellAction { ], { quotableLabel: 'Split Notebook Cell' } ); + + context.notebookEditor.cellAt(index + 1)?.updateEditState(cell.getEditState(), 'splitCell'); } } } From 91581cab6f837fb31161322a910f4c414aa40300 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 18 Dec 2024 12:53:00 -0600 Subject: [PATCH 365/479] focus debug console when it becomes visible (#236502) fix #236499 --- src/vs/workbench/contrib/debug/browser/repl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index d09086152d6..e9ffbe5a57c 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -214,6 +214,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this.tree?.updateChildren(undefined, true, false); this.onDidStyleChange(); } + this.focus(); } })); this._register(this.configurationService.onDidChangeConfiguration(e => { From 7f9c7a41873b88861744d06aed33d2dbcaa3a92e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 18 Dec 2024 11:03:57 -0800 Subject: [PATCH 366/479] testing: avoid profiles dropdown when there's a single profile (#236509) Fixes #232767 --- .../testing/browser/testingExplorerView.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 5e84db11b6a..dea0b909d04 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -346,6 +346,7 @@ export class TestingExplorerView extends ViewPane { const profileActions: IAction[] = []; let participatingGroups = 0; + let participatingProfiles = 0; let hasConfigurable = false; const defaults = this.testProfileService.getGroupDefaultProfiles(group); for (const { profiles, controller } of this.testProfileService.all()) { @@ -363,6 +364,7 @@ export class TestingExplorerView extends ViewPane { } hasConfigurable = hasConfigurable || profile.hasConfigurationHandler; + participatingProfiles++; profileActions.push(new Action( `${controller.id}.${profile.profileId}`, defaults.includes(profile) ? localize('defaultTestProfile', '{0} (Default)', profile.label) : profile.label, @@ -402,7 +404,7 @@ export class TestingExplorerView extends ViewPane { const menuActions = getFlatContextMenuActions(menu); const postActions: IAction[] = []; - if (profileActions.length > 1) { + if (participatingProfiles > 1) { postActions.push(new Action( 'selectDefaultTestConfigurations', localize('selectDefaultConfigs', 'Select Default Profile'), @@ -423,9 +425,12 @@ export class TestingExplorerView extends ViewPane { } // show menu actions if there are any otherwise don't - return menuActions.length > 0 - ? Separator.join(profileActions, menuActions, postActions) - : Separator.join(profileActions, postActions); + return { + numberOfProfiles: participatingProfiles, + actions: menuActions.length > 0 + ? Separator.join(profileActions, menuActions, postActions) + : Separator.join(profileActions, postActions), + }; } /** @@ -438,7 +443,7 @@ export class TestingExplorerView extends ViewPane { private getRunGroupDropdown(group: TestRunProfileBitset, defaultAction: IAction, options: IActionViewItemOptions) { const dropdownActions = this.getTestConfigGroupActions(group); - if (dropdownActions.length < 2) { + if (dropdownActions.numberOfProfiles < 2) { return super.getActionViewItem(defaultAction, options); } @@ -452,7 +457,7 @@ export class TestingExplorerView extends ViewPane { return this.instantiationService.createInstance( DropdownWithPrimaryActionViewItem, - primaryAction, this.getDropdownAction(), dropdownActions, + primaryAction, this.getDropdownAction(), dropdownActions.actions, '', options ); From 9fc5861de2162b2f6ab6f316c655679fdaf61eb0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:51:32 -0800 Subject: [PATCH 367/479] Speculative fix when pwsh is 'powershell' on mac/linux Fixes #219583 --- src/vs/platform/shell/node/shellEnv.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/shell/node/shellEnv.ts b/src/vs/platform/shell/node/shellEnv.ts index 2d3ef3ead0d..31a1ebe1ecf 100644 --- a/src/vs/platform/shell/node/shellEnv.ts +++ b/src/vs/platform/shell/node/shellEnv.ts @@ -129,7 +129,7 @@ async function doResolveUnixShellEnv(logService: ILogService, token: Cancellatio const name = basename(systemShellUnix); let command: string, shellArgs: Array; const extraArgs = ''; - if (/^pwsh(-preview)?$/.test(name)) { + if (/^(?:pwsh(?:-preview)|powershell)$/.test(name)) { // Older versions of PowerShell removes double quotes sometimes so we use "double single quotes" which is how // you escape single quotes inside of a single quoted string. command = `& '${process.execPath}' ${extraArgs} -p '''${mark}'' + JSON.stringify(process.env) + ''${mark}'''`; From c88542ef6216f589c6675b2aac96447b284708ef Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Wed, 18 Dec 2024 21:21:30 +0100 Subject: [PATCH 368/479] Fix navigation to single match in tree find (#236515) fixes #236478 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 38570ae882d..b8144e58c23 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1177,7 +1177,7 @@ export class FindController extends AbstractFindController this.shouldAllowFocus(node)); + this.tree.focusNext(0, true, undefined, (node) => !FuzzyScore.isDefault(node.filterData as any as FuzzyScore)); } const focus = this.tree.getFocus(); From 1147139fbb8174119528d71a99cb9a0688170999 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 18 Dec 2024 12:27:49 -0800 Subject: [PATCH 369/479] fix: copy from context menu for chat references does not work (#236518) --- .../chatReferencesContentPart.ts | 30 +++++++++++++++++-- .../files/browser/fileActions.contribution.ts | 2 +- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts index 3b71a48f0cc..a06c21d966b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts @@ -19,7 +19,8 @@ import { localize, localize2 } from '../../../../../nls.js'; import { getFlatContextMenuActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { MenuWorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; import { Action2, IMenuService, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; -import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; +import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; +import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; import { FileKind } from '../../../../../platform/files/common/files.js'; import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; @@ -476,7 +477,7 @@ registerAction2(class AddToChatAction extends Action2 { id: MenuId.ChatAttachmentsContext, group: 'chat', order: 1, - when: ExplorerFolderContext.negate(), + when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResource, ExplorerFolderContext.negate()), }] }); } @@ -498,4 +499,29 @@ registerAction2(class AddToChatAction extends Action2 { } }); +registerAction2(class OpenChatReferenceLinkAction extends Action2 { + + static readonly id = 'workbench.action.chat.copyLink'; + + constructor() { + super({ + id: OpenChatReferenceLinkAction.id, + title: { + ...localize2('copyLink', "Copy Link"), + }, + f1: false, + menu: [{ + id: MenuId.ChatAttachmentsContext, + group: 'chat', + order: 0, + when: ContextKeyExpr.or(ResourceContextKey.Scheme.isEqualTo(Schemas.http), ResourceContextKey.Scheme.isEqualTo(Schemas.https)), + }] + }); + } + + override async run(accessor: ServicesAccessor, resource: URI): Promise { + await accessor.get(IClipboardService).writeResources([resource]); + } +}); + //#endregion diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 9da55d34ce3..be7dddf26ff 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -770,7 +770,7 @@ MenuRegistry.appendMenuItem(MenuId.ChatAttachmentsContext, { group: 'navigation', order: 10, command: openToSideCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, ExplorerFolderContext.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResource, ExplorerFolderContext.toNegated()) }); MenuRegistry.appendMenuItem(MenuId.ChatAttachmentsContext, { From e6e5856995525c808ad52547beb8fe19802207ea Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 18 Dec 2024 12:39:57 -0800 Subject: [PATCH 370/479] debug: fix unexpected behaviors with duplicate `name`s in `launch.json` (#236513) Suffix duplicated launch configs with a config at the time they're read. In debug we assume the names are unique, so this should fix #231377 and probably other hidden issues as well. --- .../browser/debugConfigurationManager.ts | 61 ++++++++++++------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 0cc18f401f1..89edaf1e668 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -10,7 +10,6 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import * as json from '../../../../base/common/json.js'; import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; import { DisposableStore, IDisposable, dispose } from '../../../../base/common/lifecycle.js'; -import * as objects from '../../../../base/common/objects.js'; import * as resources from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI as uri } from '../../../../base/common/uri.js'; @@ -27,16 +26,16 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceContextService, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkbenchState } from '../../../../platform/workspace/common/workspace.js'; import { IEditorPane } from '../../../common/editor.js'; -import { debugConfigure } from './debugIcons.js'; -import { CONTEXT_DEBUG_CONFIGURATION_TYPE, DebugConfigurationProviderTriggerKind, IAdapterManager, ICompound, IConfig, IConfigPresentation, IConfigurationManager, IDebugConfigurationProvider, IGlobalConfig, IGuessedDebugger, ILaunch } from '../common/debug.js'; -import { launchSchema } from '../common/debugSchemas.js'; -import { getVisibleAndSorted } from '../common/debugUtils.js'; import { launchSchemaId } from '../../../services/configuration/common/configuration.js'; import { ACTIVE_GROUP, IEditorService } from '../../../services/editor/common/editorService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IHistoryService } from '../../../services/history/common/history.js'; import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; +import { CONTEXT_DEBUG_CONFIGURATION_TYPE, DebugConfigurationProviderTriggerKind, IAdapterManager, ICompound, IConfig, IConfigPresentation, IConfigurationManager, IDebugConfigurationProvider, IGlobalConfig, IGuessedDebugger, ILaunch } from '../common/debug.js'; +import { launchSchema } from '../common/debugSchemas.js'; +import { getVisibleAndSorted } from '../common/debugUtils.js'; +import { debugConfigure } from './debugIcons.js'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -509,7 +508,7 @@ abstract class AbstractLaunch implements ILaunch { ) { } getCompound(name: string): ICompound | undefined { - const config = this.getConfig(); + const config = this.getDeduplicatedConfig(); if (!config || !config.compounds) { return undefined; } @@ -518,7 +517,7 @@ abstract class AbstractLaunch implements ILaunch { } getConfigurationNames(ignoreCompoundsAndPresentation = false): string[] { - const config = this.getConfig(); + const config = this.getDeduplicatedConfig(); if (!config || (!Array.isArray(config.configurations) && !Array.isArray(config.compounds))) { return []; } else { @@ -540,21 +539,22 @@ abstract class AbstractLaunch implements ILaunch { getConfiguration(name: string): IConfig | undefined { // We need to clone the configuration in order to be able to make changes to it #42198 - const config = objects.deepClone(this.getConfig()); + const config = this.getDeduplicatedConfig(); if (!config || !config.configurations) { return undefined; } const configuration = config.configurations.find(config => config && config.name === name); - if (configuration) { - if (this instanceof UserLaunch) { - configuration.__configurationTarget = ConfigurationTarget.USER; - } else if (this instanceof WorkspaceLaunch) { - configuration.__configurationTarget = ConfigurationTarget.WORKSPACE; - } else { - configuration.__configurationTarget = ConfigurationTarget.WORKSPACE_FOLDER; - } + if (!configuration) { + return; + } + + if (this instanceof UserLaunch) { + return { ...configuration, __configurationTarget: ConfigurationTarget.USER }; + } else if (this instanceof WorkspaceLaunch) { + return { ...configuration, __configurationTarget: ConfigurationTarget.WORKSPACE }; + } else { + return { ...configuration, __configurationTarget: ConfigurationTarget.WORKSPACE_FOLDER }; } - return configuration; } async getInitialConfigurationContent(folderUri?: uri, type?: string, useInitialConfigs?: boolean, token?: CancellationToken): Promise { @@ -575,9 +575,28 @@ abstract class AbstractLaunch implements ILaunch { return content; } + get hidden(): boolean { return false; } + + private getDeduplicatedConfig(): IGlobalConfig | undefined { + const original = this.getConfig(); + return original && { + version: original.version, + compounds: original.compounds && distinguishConfigsByName(original.compounds), + configurations: original.configurations && distinguishConfigsByName(original.configurations), + }; + } +} + +function distinguishConfigsByName(things: readonly T[]): T[] { + const seen = new Map(); + return things.map(thing => { + const no = seen.get(thing.name) || 0; + seen.set(thing.name, no + 1); + return no === 0 ? thing : { ...thing, name: `${thing.name} (${no})` }; + }); } class Launch extends AbstractLaunch implements ILaunch { @@ -655,11 +674,9 @@ class Launch extends AbstractLaunch implements ILaunch { } async writeConfiguration(configuration: IConfig): Promise { - const fullConfig = objects.deepClone(this.getConfig()!); - if (!fullConfig.configurations) { - fullConfig.configurations = []; - } - fullConfig.configurations.push(configuration); + // note: we don't get the deduplicated config since we don't want that to 'leak' into the file + const fullConfig: Partial = this.getConfig() || {}; + fullConfig.configurations = [...fullConfig.configurations || [], configuration]; await this.configurationService.updateValue('launch', fullConfig, { resource: this.workspace.uri }, ConfigurationTarget.WORKSPACE_FOLDER); } } From 3fd6eef7b9683b6581c0111fd0304ca8e1423035 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 18 Dec 2024 13:04:11 -0800 Subject: [PATCH 371/479] fix: workbench.debug.action.focusRepl resolving before focus is given (#236520) Note: I intentionally did not keep this registered under the command palette because there is a similar duplicate command provided automatically from the views service. Fixes #228852 --- .../debug/browser/debug.contribution.ts | 3 +-- .../contrib/debug/browser/debugCommands.ts | 8 ------- .../workbench/contrib/debug/browser/repl.ts | 24 ++++++++++++++++--- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 6033b0cf9b5..fe281b6c1b1 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -31,7 +31,7 @@ import { CallStackEditorContribution } from './callStackEditorContribution.js'; import { CallStackView } from './callStackView.js'; import { ReplAccessibleView } from './replAccessibleView.js'; import { registerColors } from './debugColors.js'; -import { ADD_CONFIGURATION_ID, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, CALLSTACK_BOTTOM_ID, CALLSTACK_BOTTOM_LABEL, CALLSTACK_DOWN_ID, CALLSTACK_DOWN_LABEL, CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CALLSTACK_UP_ID, CALLSTACK_UP_LABEL, CONTINUE_ID, CONTINUE_LABEL, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, COPY_STACK_TRACE_ID, COPY_VALUE_ID, COPY_VALUE_LABEL, DEBUG_COMMAND_CATEGORY, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, EDIT_EXPRESSION_COMMAND_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, OPEN_LOADED_SCRIPTS_LABEL, PAUSE_ID, PAUSE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, REMOVE_EXPRESSION_COMMAND_ID, RESTART_FRAME_ID, RESTART_LABEL, RESTART_SESSION_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL, SET_EXPRESSION_COMMAND_ID, SHOW_LOADED_SCRIPTS_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL, TERMINATE_THREAD_ID, TOGGLE_INLINE_BREAKPOINT_ID } from './debugCommands.js'; +import { ADD_CONFIGURATION_ID, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, CALLSTACK_BOTTOM_ID, CALLSTACK_BOTTOM_LABEL, CALLSTACK_DOWN_ID, CALLSTACK_DOWN_LABEL, CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CALLSTACK_UP_ID, CALLSTACK_UP_LABEL, CONTINUE_ID, CONTINUE_LABEL, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, COPY_STACK_TRACE_ID, COPY_VALUE_ID, COPY_VALUE_LABEL, DEBUG_COMMAND_CATEGORY, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, EDIT_EXPRESSION_COMMAND_ID, JUMP_TO_CURSOR_ID, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, OPEN_LOADED_SCRIPTS_LABEL, PAUSE_ID, PAUSE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, REMOVE_EXPRESSION_COMMAND_ID, RESTART_FRAME_ID, RESTART_LABEL, RESTART_SESSION_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL, SET_EXPRESSION_COMMAND_ID, SHOW_LOADED_SCRIPTS_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL, TERMINATE_THREAD_ID, TOGGLE_INLINE_BREAKPOINT_ID } from './debugCommands.js'; import { DebugConsoleQuickAccess } from './debugConsoleQuickAccess.js'; import { RunToCursorAction, SelectionToReplAction, SelectionToWatchExpressionsAction } from './debugEditorActions.js'; import { DebugEditorContribution } from './debugEditorContribution.js'; @@ -133,7 +133,6 @@ registerDebugCommandPaletteItem(DISCONNECT_ID, DISCONNECT_LABEL, CONTEXT_IN_DEBU registerDebugCommandPaletteItem(DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.or(CONTEXT_FOCUSED_SESSION_IS_ATTACH, ContextKeyExpr.and(CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED))); registerDebugCommandPaletteItem(STOP_ID, STOP_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.or(CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated(), CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED)); registerDebugCommandPaletteItem(CONTINUE_ID, CONTINUE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCommandPaletteItem(FOCUS_REPL_ID, nls.localize2({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, "Focus on Debug Console View")); registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize2('jumpToCursor', "Jump to Cursor"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize2('SetNextStatement', "Set Next Statement"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); registerDebugCommandPaletteItem(RunToCursorAction.ID, RunToCursorAction.LABEL, CONTEXT_DEBUGGERS_AVAILABLE); diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index c6d45bda962..2d3cb0c46c8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -679,14 +679,6 @@ CommandsRegistry.registerCommand({ } }); -CommandsRegistry.registerCommand({ - id: FOCUS_REPL_ID, - handler: async (accessor) => { - const viewsService = accessor.get(IViewsService); - await viewsService.openView(REPL_VIEW_ID, true); - } -}); - CommandsRegistry.registerCommand({ id: 'debug.startFromConfig', handler: async (accessor, config: IConfig) => { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index e9ffbe5a57c..29504942045 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -11,7 +11,7 @@ import * as aria from '../../../../base/browser/ui/aria/aria.js'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from '../../../../base/browser/ui/mouseCursor/mouseCursor.js'; import { IAsyncDataSource, ITreeContextMenuEvent, ITreeNode } from '../../../../base/browser/ui/tree/tree.js'; import { IAction } from '../../../../base/common/actions.js'; -import { RunOnceScheduler } from '../../../../base/common/async.js'; +import { RunOnceScheduler, timeout } from '../../../../base/common/async.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { memoize } from '../../../../base/common/decorators.js'; @@ -71,6 +71,7 @@ import { CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_REPL, CONTEXT_MULTI_SESSION_REPL, import { Variable } from '../common/debugModel.js'; import { ReplEvaluationResult, ReplGroup } from '../common/replModel.js'; import { FocusSessionActionViewItem } from './debugActionViewItems.js'; +import { DEBUG_COMMAND_CATEGORY, FOCUS_REPL_ID } from './debugCommands.js'; import { DebugExpressionRenderer } from './debugExpressionRenderer.js'; import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from './debugIcons.js'; import './media/repl.css'; @@ -554,9 +555,10 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this.tree?.domFocus(); } - override focus(): void { + override async focus(): Promise { super.focus(); - setTimeout(() => this.replInput.focus(), 0); + await timeout(0); // wait a task for the repl to get attached to the DOM, #83387 + this.replInput.focus(); } override getActionViewItem(action: IAction): IActionViewItem | undefined { @@ -1209,3 +1211,19 @@ registerAction2(class extends Action2 { } } }); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: FOCUS_REPL_ID, + category: DEBUG_COMMAND_CATEGORY, + title: localize2({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, "Focus on Debug Console View"), + }); + } + + override async run(accessor: ServicesAccessor) { + const viewsService = accessor.get(IViewsService); + const repl = await viewsService.openView(REPL_VIEW_ID); + await repl?.focus(); + } +}); From 37c543ba48a33b8113f514951a8b3e39a04fd6bc Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 18 Dec 2024 22:26:30 +0100 Subject: [PATCH 372/479] Implements gutter indicator (off by default). (#236522) * Implements gutter indicator (off by default). * Adds isSingleLine method to monaco Range * update --- src/vs/editor/browser/observableCodeEditor.ts | 16 ++ src/vs/editor/browser/rect.ts | 135 +++++++++++++ src/vs/editor/common/config/editorOptions.ts | 8 + src/vs/editor/common/core/offsetRange.ts | 4 + .../browser/model/inlineCompletionsModel.ts | 2 +- .../view/inlineEdits/gutterIndicatorView.ts | 189 ++++++++++++++++++ .../browser/view/inlineEdits/indicatorView.ts | 4 +- .../browser/view/inlineEdits/utils.ts | 10 +- .../browser/view/inlineEdits/view.ts | 39 ++-- src/vs/monaco.d.ts | 1 + 10 files changed, 388 insertions(+), 20 deletions(-) create mode 100644 src/vs/editor/browser/rect.ts create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index 781a9895212..d8607ae319a 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -7,6 +7,8 @@ import { equalsIfDefined, itemsEquals } from '../../base/common/equals.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js'; import { IObservable, 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 { OffsetRange } from '../common/core/offsetRange.js'; import { Position } from '../common/core/position.js'; import { Selection } from '../common/core/selection.js'; import { ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; @@ -265,6 +267,20 @@ export class ObservableCodeEditor extends Disposable { }); } + public observeLineOffsetRange(lineRange: IObservable, store: DisposableStore): IObservable { + const start = this.observePosition(lineRange.map(r => new Position(r.startLineNumber, 1)), store); + const end = this.observePosition(lineRange.map(r => new Position(r.endLineNumberExclusive + 1, 1)), store); + + return derived(reader => { + start.read(reader); + end.read(reader); + const range = lineRange.read(reader); + const s = this.editor.getTopForLineNumber(range.startLineNumber) - this.scrollTop.read(reader); + const e = range.isEmpty ? s : (this.editor.getBottomForLineNumber(range.endLineNumberExclusive - 1) - this.scrollTop.read(reader)); + return new OffsetRange(s, e); + }); + } + public observePosition(position: IObservable, store: DisposableStore): IObservable { let pos = position.get(); const result = observableValueOpts({ owner: this, debugName: () => `topLeftOfPosition${pos?.toString()}`, equalsFn: equalsIfDefined(Point.equals) }, new Point(0, 0)); diff --git a/src/vs/editor/browser/rect.ts b/src/vs/editor/browser/rect.ts new file mode 100644 index 00000000000..b990944eb67 --- /dev/null +++ b/src/vs/editor/browser/rect.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------------------------- + * 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 '../common/core/offsetRange.js'; +import { Point } from './point.js'; + +export class Rect { + public static fromPoint(point: Point): Rect { + return new Rect(point.x, point.y, point.x, point.y); + } + + public static fromLeftTopRightBottom(left: number, top: number, right: number, bottom: number): Rect { + return new Rect(left, top, right, bottom); + } + + public static fromLeftTopWidthHeight(left: number, top: number, width: number, height: number): Rect { + return new Rect(left, top, left + width, top + height); + } + + public static fromRanges(leftRight: OffsetRange, topBottom: OffsetRange): Rect { + return new Rect(leftRight.start, topBottom.start, leftRight.endExclusive, topBottom.endExclusive); + } + + public static hull(rects: Rect[]): Rect { + let left = Number.MAX_VALUE; + let top = Number.MAX_VALUE; + let right = Number.MIN_VALUE; + let bottom = Number.MIN_VALUE; + + for (const rect of rects) { + left = Math.min(left, rect.left); + top = Math.min(top, rect.top); + right = Math.max(right, rect.right); + bottom = Math.max(bottom, rect.bottom); + } + + return new Rect(left, top, right, bottom); + } + + public readonly width = this.right - this.left; + public readonly height = this.bottom - this.top; + + constructor( + public readonly left: number, + public readonly top: number, + public readonly right: number, + public readonly bottom: number, + ) { + if (left > right || top > bottom) { + throw new Error('Invalid arguments'); + } + } + + withMargin(margin: number): Rect { + return new Rect(this.left - margin, this.top - margin, this.right + margin, this.bottom + margin); + } + + intersectVertical(range: OffsetRange): Rect { + return new Rect( + this.left, + Math.max(this.top, range.start), + this.right, + Math.min(this.bottom, range.endExclusive), + ); + } + + toString(): string { + return `Rect{(${this.left},${this.top}), (${this.right},${this.bottom})}`; + } + + intersect(parent: Rect): Rect | undefined { + const left = Math.max(this.left, parent.left); + const right = Math.min(this.right, parent.right); + const top = Math.max(this.top, parent.top); + const bottom = Math.min(this.bottom, parent.bottom); + + if (left > right || top > bottom) { + return undefined; + } + + return new Rect(left, top, right, bottom); + } + + union(other: Rect): Rect { + return new Rect( + Math.min(this.left, other.left), + Math.min(this.top, other.top), + Math.max(this.right, other.right), + Math.max(this.bottom, other.bottom), + ); + } + + containsRect(other: Rect): boolean { + return this.left <= other.left + && this.top <= other.top + && this.right >= other.right + && this.bottom >= other.bottom; + } + + moveToBeContainedIn(parent: Rect): Rect { + const width = this.width; + const height = this.height; + + let left = this.left; + let top = this.top; + + if (left < parent.left) { + left = parent.left; + } else if (left + width > parent.right) { + left = parent.right - width; + } + + if (top < parent.top) { + top = parent.top; + } else if (top + height > parent.bottom) { + top = parent.bottom - height; + } + + return new Rect(left, top, left + width, top + height); + } + + withWidth(width: number): Rect { + return new Rect(this.left, this.top, this.left + width, this.bottom); + } + + withHeight(height: number): Rect { + return new Rect(this.left, this.top, this.right, this.top + height); + } + + withTop(top: number): Rect { + return new Rect(this.left, top, this.right, this.bottom); + } +} diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 0c255963581..c7481985f2f 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4198,6 +4198,7 @@ export interface IInlineSuggestOptions { useMixedLinesDiff?: 'never' | 'whenPossible' | 'forStableInsertions' | 'afterJumpWhenPossible'; useInterleavedLinesDiff?: 'never' | 'always' | 'afterJump'; onlyShowWhenCloseToCursor?: boolean; + useGutterIndicator?: boolean; }; }; } @@ -4230,6 +4231,7 @@ class InlineEditorSuggest extends BaseEditorOption { await this._deltaSelectedInlineCompletionIndex(-1); } - public async accept(editor: ICodeEditor): Promise { + public async accept(editor: ICodeEditor = this._editor): Promise { if (editor.getModel() !== this.textModel) { throw new BugIndicatingError(); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts new file mode 100644 index 00000000000..936ab8b56b3 --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -0,0 +1,189 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { Codicon } from '../../../../../../base/common/codicons.js'; +import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { IObservable, IReader, constObservable, derived, observableFromEvent } from '../../../../../../base/common/observable.js'; +import { buttonBackground, buttonForeground, buttonSecondaryBackground, buttonSecondaryForeground } from '../../../../../../platform/theme/common/colorRegistry.js'; +import { registerColor, transparent } from '../../../../../../platform/theme/common/colorUtils.js'; +import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { Rect } from '../../../../../browser/rect.js'; +import { EditorOption } from '../../../../../common/config/editorOptions.js'; +import { LineRange } from '../../../../../common/core/lineRange.js'; +import { OffsetRange } from '../../../../../common/core/offsetRange.js'; +import { StickyScrollController } from '../../../../stickyScroll/browser/stickyScrollController.js'; +import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import { mapOutFalsy, n } from './utils.js'; + +export const inlineEditIndicatorPrimaryForeground = registerColor('inlineEdit.gutterIndicator.primaryForeground', buttonForeground, 'Foreground color for the primary inline edit gutter indicator.'); +export const inlineEditIndicatorPrimaryBackground = registerColor('inlineEdit.gutterIndicator.primaryBackground', buttonBackground, 'Background color for the primary inline edit gutter indicator.'); + +export const inlineEditIndicatorSecondaryForeground = registerColor('inlineEdit.gutterIndicator.secondaryForeground', buttonSecondaryForeground, 'Foreground color for the secondary inline edit gutter indicator.'); +export const inlineEditIndicatorSecondaryBackground = registerColor('inlineEdit.gutterIndicator.secondaryBackground', buttonSecondaryBackground, 'Background color for the secondary inline edit gutter indicator.'); + +export const inlineEditIndicatorsuccessfulForeground = registerColor('inlineEdit.gutterIndicator.successfulForeground', buttonForeground, 'Foreground color for the successful inline edit gutter indicator.'); +export const inlineEditIndicatorsuccessfulBackground = registerColor('inlineEdit.gutterIndicator.successfulBackground', { light: '#2e825c', dark: '#2e825c', hcLight: '#2e825c', hcDark: '#2e825c' }, 'Background color for the successful inline edit gutter indicator.'); + +export const inlineEditIndicatorBackground = registerColor( + 'inlineEdit.gutterIndicator.background', + { + hcDark: transparent('tab.inactiveBackground', 0.5), + hcLight: transparent('tab.inactiveBackground', 0.5), + dark: transparent('tab.inactiveBackground', 0.5), + light: '#5f5f5f18', + }, + 'Background color for the inline edit gutter indicator.' +); + + +export class InlineEditsGutterIndicator extends Disposable { + private readonly _state = derived(reader => { + const range = mapOutFalsy(this._originalRange).read(reader); + if (!range) { + return undefined; + } + + return { + range, + lineOffsetRange: this._editorObs.observeLineOffsetRange(range, this._store), + }; + }); + + private _stickyScrollController = StickyScrollController.get(this._editorObs.editor); + private readonly _stickyScrollHeight = this._stickyScrollController ? observableFromEvent(this._stickyScrollController.onDidChangeStickyScrollHeight, () => this._stickyScrollController!.stickyScrollWidgetHeight) : constObservable(0); + + + private readonly _layout = derived(reader => { + const s = this._state.read(reader); + if (!s) { return undefined; } + + const layout = this._editorObs.layoutInfo.read(reader); + + const fullViewPort = Rect.fromLeftTopRightBottom(0, 0, layout.width, layout.height); + const viewPortWithStickyScroll = fullViewPort.withTop(this._stickyScrollHeight.read(reader)); + + const targetVertRange = s.lineOffsetRange.read(reader); + + const space = 1; + + const targetRect = Rect.fromRanges(OffsetRange.fromTo(space, layout.lineNumbersLeft + layout.lineNumbersWidth + 4), targetVertRange); + + + const lineHeight = this._editorObs.getOption(EditorOption.lineHeight).read(reader); + const pillRect = targetRect.withHeight(lineHeight).withWidth(22); + const pillRectMoved = pillRect.moveToBeContainedIn(viewPortWithStickyScroll); + + const rect = targetRect; + + const iconRect = (targetRect.containsRect(pillRectMoved)) + ? pillRectMoved + : pillRectMoved.moveToBeContainedIn(fullViewPort.intersect(targetRect.union(fullViewPort.withHeight(lineHeight)))!); //viewPortWithStickyScroll.intersect(rect)!; + + return { + rect, + iconRect, + mode: (iconRect.top === targetRect.top ? 'right' as const + : iconRect.top > targetRect.top ? 'top' as const : 'bottom' as const), + docked: rect.containsRect(iconRect) && viewPortWithStickyScroll.containsRect(iconRect), + }; + }); + + private readonly _mode = derived(this, reader => { + const m = this._model.read(reader); + if (m && m.tabShouldAcceptInlineEdit.read(reader)) { return 'accept' as const; } + if (m && m.tabShouldJumpToInlineEdit.read(reader)) { return 'jump' as const; } + return 'inactive' as const; + }); + + private readonly _onClickAction = derived(this, reader => { + if (this._layout.map(d => d && d.docked).read(reader)) { + return { + label: 'Click to accept inline edit', + action: () => { this._model.get()?.accept(); } + }; + } else { + return { + label: 'Click to jump to inline edit', + action: () => { this._model.get()?.jump(); } + }; + } + }); + + private readonly _indicator = n.div({ + class: 'inline-edits-view-gutter-indicator', + onclick: () => this._onClickAction.get().action(), + title: this._onClickAction.map(a => a.label), + style: { + position: 'absolute', + overflow: 'visible', + }, + }, mapOutFalsy(this._layout).map(l => !l ? [] : [ + n.div({ + style: { + position: 'absolute', + background: 'var(--vscode-inlineEdit-gutterIndicator-background)', + borderRadius: '4px', + ...rectToProps(reader => l.read(reader).rect), + } + }), + n.div({ + class: 'icon', + style: { + cursor: 'pointer', + zIndex: '1000', + position: 'absolute', + backgroundColor: this._mode.map(v => ({ + inactive: 'var(--vscode-inlineEdit-gutterIndicator-secondaryBackground)', + jump: 'var(--vscode-inlineEdit-gutterIndicator-primaryBackground)', + accept: 'var(--vscode-inlineEdit-gutterIndicator-successfulBackground)', + }[v])), + '--vscodeIconForeground': this._mode.map(v => ({ + inactive: 'var(--vscode-inlineEdit-gutterIndicator-secondaryForeground)', + jump: 'var(--vscode-inlineEdit-gutterIndicator-primaryForeground)', + accept: 'var(--vscode-inlineEdit-gutterIndicator-successfulForeground)', + }[v])), + borderRadius: '4px', + display: 'flex', + justifyContent: 'center', + transition: 'background-color 0.2s ease-in-out', + ...rectToProps(reader => l.read(reader).iconRect), + } + }, [ + n.div({ + style: { + rotate: l.map(l => ({ right: '0deg', bottom: '90deg', top: '-90deg' }[l.mode])), + transition: 'rotate 0.2s ease-in-out', + } + }, [ + renderIcon(Codicon.arrowRight), + ]) + ]), + ])).keepUpdated(this._store); + + constructor( + private readonly _editorObs: ObservableCodeEditor, + private readonly _originalRange: IObservable, + private readonly _model: IObservable, + ) { + super(); + + this._register(this._editorObs.createOverlayWidget({ + domNode: this._indicator.element, + position: constObservable(null), + allowEditorOverflow: false, + minContentWidthInPx: constObservable(0), + })); + } +} + +function rectToProps(fn: (reader: IReader) => Rect): any { + return { + left: derived(reader => fn(reader).left), + top: derived(reader => fn(reader).top), + width: derived(reader => fn(reader).right - fn(reader).left), + height: derived(reader => fn(reader).bottom - fn(reader).top), + }; +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts index 052451f326d..f5845663658 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts @@ -19,11 +19,9 @@ export interface IInlineEditsIndicatorState { showAlways: boolean; } -// editorHoverForeground + export const inlineEditIndicatorForeground = registerColor('inlineEdit.indicator.foreground', buttonForeground, ''); -// editorHoverBackground export const inlineEditIndicatorBackground = registerColor('inlineEdit.indicator.background', buttonBackground, ''); -// editorHoverBorder export const inlineEditIndicatorBorder = registerColor('inlineEdit.indicator.border', buttonSeparator, ''); export class InlineEditsIndicator extends Disposable { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index d9928a05b2e..73b9cbb5618 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -9,7 +9,7 @@ import { numberComparator } from '../../../../../../base/common/arrays.js'; import { findFirstMin } from '../../../../../../base/common/arraysFind.js'; import { BugIndicatingError } from '../../../../../../base/common/errors.js'; import { Disposable, DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; -import { derived, IObservable, IReader, observableValue, transaction } from '../../../../../../base/common/observable.js'; +import { derived, derivedObservableWithCache, IObservable, IReader, observableValue, transaction } from '../../../../../../base/common/observable.js'; import { OS } from '../../../../../../base/common/platform.js'; import { getIndentationLength, splitLines } from '../../../../../../base/common/strings.js'; import { URI } from '../../../../../../base/common/uri.js'; @@ -329,6 +329,8 @@ export abstract class ObserverNode extends Disposab } else { this._element.tabIndex = value; } + } else if (key.startsWith('on')) { + (this._element as any)[key] = value; } else { if (isObservable(value)) { this._deriveds.push(derived(this, reader => { @@ -425,12 +427,16 @@ type ElementAttributeKeys = Partial<{ }>; export function mapOutFalsy(obs: IObservable): IObservable | undefined | null | false> { + const nonUndefinedObs = derivedObservableWithCache(undefined, (reader, lastValue) => obs.read(reader) || lastValue); + return derived(reader => { + nonUndefinedObs.read(reader); const val = obs.read(reader); if (!val) { return undefined; } - return obs as IObservable; + + return nonUndefinedObs as IObservable; }); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts index 945c91b9e13..68bc963e374 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { derived, IObservable, IReader } from '../../../../../../base/common/observable.js'; +import { autorunWithStore, derived, IObservable, IReader } from '../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; @@ -15,6 +15,7 @@ import { StringText } from '../../../../../common/core/textEdit.js'; import { DetailedLineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { TextModel } from '../../../../../common/model/textModel.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import { InlineEditsGutterIndicator } from './gutterIndicatorView.js'; import { IInlineEditsIndicatorState, InlineEditsIndicator } from './indicatorView.js'; import { IOriginalEditorInlineDiffViewState, OriginalEditorInlineDiffView } from './inlineDiffView.js'; import { InlineEditsSideBySideDiff } from './sideBySideDiff.js'; @@ -118,19 +119,29 @@ export class InlineEditsView extends Disposable { protected readonly _inlineDiffView = this._register(new OriginalEditorInlineDiffView(this._editor, this._inlineDiffViewState, this._previewTextModel)); - protected readonly _indicator = this._register(new InlineEditsIndicator( - this._editorObs, - derived(reader => { - const state = this._uiState.read(reader); - if (!state) { return undefined; } - - const range = state.originalDisplayRange; - const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); - - return { editTop: top, showAlways: state.state !== 'sideBySide' }; - }), - this._model, - )); + private readonly _useGutterIndicator = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useGutterIndicator); + + protected readonly _indicator = this._register(autorunWithStore((reader, store) => { + if (this._useGutterIndicator.read(reader)) { + store.add(new InlineEditsGutterIndicator( + this._editorObs, + this._uiState.map(s => s && s.originalDisplayRange), + this._model, + )); + } else { + store.add(new InlineEditsIndicator( + this._editorObs, + derived(reader => { + const state = this._uiState.read(reader); + if (!state) { return undefined; } + const range = state.originalDisplayRange; + const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); + return { editTop: top, showAlways: state.state !== 'sideBySide' }; + }), + this._model, + )); + } + })); private determinRenderState(edit: InlineEditWithChanges, reader: IReader, diff: DetailedLineRangeMapping[]) { if (edit.isCollapsed) { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 77cd7ae49a5..7b6d78e8552 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4605,6 +4605,7 @@ declare namespace monaco.editor { useMixedLinesDiff?: 'never' | 'whenPossible' | 'forStableInsertions' | 'afterJumpWhenPossible'; useInterleavedLinesDiff?: 'never' | 'always' | 'afterJump'; onlyShowWhenCloseToCursor?: boolean; + useGutterIndicator?: boolean; }; }; } From 0900a621135265c95c0a499b5123e2cae848ae1f Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 18 Dec 2024 15:40:29 -0600 Subject: [PATCH 373/479] get terminal completions to work for screen reader users (#236516) fix #235022 --- .../suggest/browser/simpleSuggestWidget.ts | 72 ++++++++++++++++++- .../browser/simpleSuggestWidgetRenderer.ts | 2 +- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts index 61fb7437cea..bc9c9ea0cba 100644 --- a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts +++ b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts @@ -10,9 +10,9 @@ import { List } from '../../../../base/browser/ui/list/listWidget.js'; import { ResizableHTMLElement } from '../../../../base/browser/ui/resizable/resizable.js'; import { SimpleCompletionItem } from './simpleCompletionItem.js'; import { LineContext, SimpleCompletionModel } from './simpleCompletionModel.js'; -import { SimpleSuggestWidgetItemRenderer, type ISimpleSuggestWidgetFontInfo } from './simpleSuggestWidgetRenderer.js'; +import { getAriaId, SimpleSuggestWidgetItemRenderer, type ISimpleSuggestWidgetFontInfo } from './simpleSuggestWidgetRenderer.js'; import { TimeoutTimer } from '../../../../base/common/async.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; +import { Emitter, Event, PauseableEmitter } from '../../../../base/common/event.js'; import { MutableDisposable, Disposable } from '../../../../base/common/lifecycle.js'; import { clamp } from '../../../../base/common/numbers.js'; import { localize } from '../../../../nls.js'; @@ -68,7 +68,9 @@ export class SimpleSuggestWidget extends Disposable { private _forceRenderingAbove: boolean = false; private _preference?: WidgetPositionPreference; private readonly _pendingLayout = this._register(new MutableDisposable()); - + // private _currentSuggestionDetails?: CancelablePromise; + private _focusedItem?: SimpleCompletionItem; + private _ignoreFocusEvents: boolean = false; readonly element: ResizableHTMLElement; private readonly _messageElement: HTMLElement; private readonly _listElement: HTMLElement; @@ -83,6 +85,8 @@ export class SimpleSuggestWidget extends Disposable { readonly onDidHide: Event = this._onDidHide.event; private readonly _onDidShow = this._register(new Emitter()); readonly onDidShow: Event = this._onDidShow.event; + private readonly _onDidFocus = new PauseableEmitter(); + readonly onDidFocus: Event = this._onDidFocus.event; get list(): List { return this._list; } @@ -206,6 +210,7 @@ export class SimpleSuggestWidget extends Disposable { this._register(this._list.onMouseDown(e => this._onListMouseDownOrTap(e))); this._register(this._list.onTap(e => this._onListMouseDownOrTap(e))); + this._register(this._list.onDidChangeFocus(e => this._onListFocus(e))); this._register(this._list.onDidChangeSelection(e => this._onListSelection(e))); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor.suggest.showIcons')) { @@ -214,6 +219,67 @@ export class SimpleSuggestWidget extends Disposable { })); } + private _onListFocus(e: IListEvent): void { + if (this._ignoreFocusEvents) { + return; + } + + if (this._state === State.Details) { + // This can happen when focus is in the details-panel and when + // arrow keys are pressed to select next/prev items + this._setState(State.Open); + } + + if (!e.elements.length) { + // if (this._currentSuggestionDetails) { + // this._currentSuggestionDetails.cancel(); + // this._currentSuggestionDetails = undefined; + // this._focusedItem = undefined; + // } + this._clearAriaActiveDescendant(); + return; + } + + if (!this._completionModel) { + return; + } + + // this._ctxSuggestWidgetHasFocusedSuggestion.set(true); + const item = e.elements[0]; + const index = e.indexes[0]; + + if (item !== this._focusedItem) { + + // this._currentSuggestionDetails?.cancel(); + // this._currentSuggestionDetails = undefined; + + this._focusedItem = item; + + this._list.reveal(index); + const id = getAriaId(index); + const node = dom.getActiveWindow().document.activeElement; + if (node && id) { + node.setAttribute('aria-haspopup', 'true'); + node.setAttribute('aria-autocomplete', 'list'); + node.setAttribute('aria-activedescendant', id); + } else { + this._clearAriaActiveDescendant(); + } + } + // emit an event + this._onDidFocus.fire({ item, index, model: this._completionModel }); + } + + private _clearAriaActiveDescendant(): void { + const node = dom.getActiveWindow().document.activeElement; + if (!node) { + return; + } + node.setAttribute('aria-haspopup', 'false'); + node.setAttribute('aria-autocomplete', 'both'); + node.removeAttribute('aria-activedescendant'); + } + private _cursorPosition?: { top: number; left: number; height: number }; setCompletionModel(completionModel: SimpleCompletionModel) { diff --git a/src/vs/workbench/services/suggest/browser/simpleSuggestWidgetRenderer.ts b/src/vs/workbench/services/suggest/browser/simpleSuggestWidgetRenderer.ts index 86e0f510b0f..83605e2156d 100644 --- a/src/vs/workbench/services/suggest/browser/simpleSuggestWidgetRenderer.ts +++ b/src/vs/workbench/services/suggest/browser/simpleSuggestWidgetRenderer.ts @@ -14,7 +14,7 @@ import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; export function getAriaId(index: number): string { - return `simple-suggest-aria-id:${index}`; + return `simple-suggest-aria-id-${index}`; } export interface ISimpleSuggestionTemplateData { From 4572abc566c41ae926fcd01c979f9e312c69a7ca Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 18 Dec 2024 13:51:49 -0800 Subject: [PATCH 374/479] debug: fix REPL input not resizing when pasting content that wraps (#236519) Fixes #229541 --- src/vs/workbench/contrib/debug/browser/repl.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 29504942045..4bcfe48be3e 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -109,7 +109,6 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { private replInput!: CodeEditorWidget; private replInputContainer!: HTMLElement; private bodyContentDimension: dom.Dimension | undefined; - private replInputLineCount = 1; private model: ITextModel | undefined; private setHistoryNavigationEnablement!: (enabled: boolean) => void; private scopedInstantiationService!: IInstantiationService; @@ -476,9 +475,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { revealLastElement(this.tree!); this.history.add(this.replInput.getValue()); this.replInput.setValue(''); - const shouldRelayout = this.replInputLineCount > 1; - this.replInputLineCount = 1; - if (shouldRelayout && this.bodyContentDimension) { + if (this.bodyContentDimension) { // Trigger a layout to shrink a potential multi line input this.layoutBodyContent(this.bodyContentDimension.height, this.bodyContentDimension.width); } @@ -737,12 +734,14 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this.replInput = this.scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, options, getSimpleCodeEditorWidgetOptions()); + let lastContentHeight = -1; this._register(this.replInput.onDidChangeModelContent(() => { const model = this.replInput.getModel(); this.setHistoryNavigationEnablement(!!model && model.getValue() === ''); - const lineCount = model ? Math.min(10, model.getLineCount()) : 1; - if (lineCount !== this.replInputLineCount) { - this.replInputLineCount = lineCount; + + const contentHeight = this.replInput.getContentHeight(); + if (contentHeight !== lastContentHeight) { + lastContentHeight = contentHeight; if (this.bodyContentDimension) { this.layoutBodyContent(this.bodyContentDimension.height, this.bodyContentDimension.width); } From a7d5ca7e7d5d97e6cefa9fc9f4d16950829bdfd7 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Wed, 18 Dec 2024 14:12:51 -0800 Subject: [PATCH 375/479] Fix logic for notebook outline data source isEmpty() fn (#236525) amend logic for notebook outline data source isEmpty() --- .../contrib/outline/notebookOutline.ts | 61 ++++++++++++------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts index 7df78960c43..0e3812f9f8b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts @@ -346,6 +346,28 @@ export class NotebookQuickPickProvider implements IQuickPickDataSource NotebookOutlineConstants.NonHeaderOutlineLevel) // show symbols off + cell is code + is level >7 (nb symbol levels) + ) { + return true; + } + + return false; +} + export class NotebookOutlinePaneProvider implements IDataSource { private readonly _disposables = new DisposableStore(); @@ -381,14 +403,14 @@ export class NotebookOutlinePaneProvider implements IDataSource NotebookOutlineConstants.NonHeaderOutlineLevel) // show symbols off + cell is code + is level >7 (nb symbol levels) - ) { - return true; - } - - return false; - } - *getChildren(element: NotebookCellOutline | OutlineEntry): Iterable { const isOutline = element instanceof NotebookCellOutline; const entries = isOutline ? this.outlineDataSourceRef?.object?.entries ?? [] : element.children; @@ -518,7 +521,9 @@ export class NotebookCellOutline implements IOutline { private _breadcrumbsDataSource!: IBreadcrumbsDataSource; // view settings + private outlineShowCodeCells: boolean; private outlineShowCodeCellSymbols: boolean; + private outlineShowMarkdownHeadersOnly: boolean; // getters get activeElement(): OutlineEntry | undefined { @@ -538,7 +543,13 @@ export class NotebookCellOutline implements IOutline { return this._outlineDataSourceReference?.object?.uri; } get isEmpty(): boolean { - return this._outlineDataSourceReference?.object?.isEmpty ?? true; + if (!this._outlineDataSourceReference?.object?.entries) { + return true; + } + + return !this._outlineDataSourceReference.object.entries.some(entry => { + return !filterEntry(entry, this.outlineShowMarkdownHeadersOnly, this.outlineShowCodeCells, this.outlineShowCodeCellSymbols); + }); } private checkDelayer() { @@ -558,7 +569,9 @@ export class NotebookCellOutline implements IOutline { @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService, ) { + this.outlineShowCodeCells = this._configurationService.getValue(NotebookSetting.outlineShowCodeCells); this.outlineShowCodeCellSymbols = this._configurationService.getValue(NotebookSetting.outlineShowCodeCellSymbols); + this.outlineShowMarkdownHeadersOnly = this._configurationService.getValue(NotebookSetting.outlineShowMarkdownHeadersOnly); this.initializeOutline(); @@ -615,6 +628,10 @@ export class NotebookCellOutline implements IOutline { e.affectsConfiguration(NotebookSetting.outlineShowCodeCellSymbols) || e.affectsConfiguration(NotebookSetting.breadcrumbsShowCodeCells) ) { + this.outlineShowCodeCells = this._configurationService.getValue(NotebookSetting.outlineShowCodeCells); + this.outlineShowCodeCellSymbols = this._configurationService.getValue(NotebookSetting.outlineShowCodeCellSymbols); + this.outlineShowMarkdownHeadersOnly = this._configurationService.getValue(NotebookSetting.outlineShowMarkdownHeadersOnly); + this.delayedRecomputeState(); } })); From 6869b35a03738298550ab62968cc817ab15828e3 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 18 Dec 2024 14:16:19 -0800 Subject: [PATCH 376/479] Show chat participants and tools on extension features page (#236526) * Show chat participants and tools on extension features page * Remove "ID" --- .../platform/extensions/common/extensions.ts | 19 ++++++- .../browser/chatParticipant.contribution.ts | 54 +++++++++++++++++- .../common/chatParticipantContribTypes.ts | 1 - .../tools/languageModelToolsContribution.ts | 55 ++++++++++++++++++- 4 files changed, 122 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 687fd50dc36..d6758a9b54d 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -167,6 +167,22 @@ export interface ILocalizationContribution { minimalTranslations?: { [key: string]: string }; } +export interface IChatParticipantContribution { + id: string; + name: string; + fullName: string; + description?: string; + isDefault?: boolean; + commands?: { name: string }[]; +} + +export interface IToolContribution { + name: string; + displayName: string; + modelDescription: string; + userDescription?: string; +} + export interface IExtensionContributions { commands?: ICommand[]; configuration?: any; @@ -192,7 +208,8 @@ export interface IExtensionContributions { readonly notebooks?: INotebookEntry[]; readonly notebookRenderer?: INotebookRendererContribution[]; readonly debugVisualizers?: IDebugVisualizationContribution[]; - readonly chatParticipants?: ReadonlyArray<{ id: string }>; + readonly chatParticipants?: ReadonlyArray; + readonly languageModelTools?: ReadonlyArray; } export interface IExtensionCapabilities { diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts index 6326c643d9c..3d16ee993b7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts @@ -7,12 +7,13 @@ import { coalesce, isNonEmptyArray } from '../../../../base/common/arrays.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { toErrorMessage } from '../../../../base/common/errorMessage.js'; import { Event } from '../../../../base/common/event.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import * as strings from '../../../../base/common/strings.js'; import { localize, localize2 } from '../../../../nls.js'; import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; +import { ExtensionIdentifier, IExtensionManifest } from '../../../../platform/extensions/common/extensions.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; @@ -20,6 +21,7 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer, ViewContainerLocation, Extensions as ViewExtensions } from '../../../common/views.js'; +import { IExtensionFeatureTableRenderer, IRenderedData, ITableData, IRowData, IExtensionFeaturesRegistry, Extensions } from '../../../services/extensionManagement/common/extensionFeatures.js'; import { isProposedApiEnabled } from '../../../services/extensions/common/extensions.js'; import * as extensionsRegistry from '../../../services/extensions/common/extensionsRegistry.js'; import { showExtensionsWithIdsCommandId } from '../../extensions/browser/extensionsActions.js'; @@ -202,7 +204,7 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { continue; } - if ((providerDescriptor.defaultImplicitVariables || providerDescriptor.locations) && !isProposedApiEnabled(extension.description, 'chatParticipantAdditions')) { + if (providerDescriptor.locations && !isProposedApiEnabled(extension.description, 'chatParticipantAdditions')) { this.logService.error(`Extension '${extension.description.identifier.value}' CANNOT use API proposal: chatParticipantAdditions.`); continue; } @@ -423,3 +425,51 @@ export class ChatCompatibilityNotifier extends Disposable implements IWorkbenchC })); } } + +class ChatParticipantDataRenderer extends Disposable implements IExtensionFeatureTableRenderer { + readonly type = 'table'; + + shouldRender(manifest: IExtensionManifest): boolean { + return !!manifest.contributes?.chatParticipants; + } + + render(manifest: IExtensionManifest): IRenderedData { + const nonDefaultContributions = manifest.contributes?.chatParticipants?.filter(c => !c.isDefault) ?? []; + if (!nonDefaultContributions.length) { + return { data: { headers: [], rows: [] }, dispose: () => { } }; + } + + const headers = [ + localize('participantName', "Name"), + localize('participantFullName', "Full Name"), + localize('participantDescription', "Description"), + localize('participantCommands', "Commands"), + ]; + + const rows: IRowData[][] = nonDefaultContributions.map(d => { + return [ + '@' + d.name, + d.fullName, + d.description ?? '-', + d.commands?.length ? new MarkdownString(d.commands.map(c => `- /` + c.name).join('\n')) : '-' + ]; + }); + + return { + data: { + headers, + rows + }, + dispose: () => { } + }; + } +} + +Registry.as(Extensions.ExtensionFeaturesRegistry).registerExtensionFeature({ + id: 'chatParticipants', + label: localize('chatParticipants', "Chat Participants"), + access: { + canToggle: false + }, + renderer: new SyncDescriptor(ChatParticipantDataRenderer), +}); diff --git a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts index e17e9a1322f..211d7e7e9c9 100644 --- a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts @@ -25,7 +25,6 @@ export interface IRawChatParticipantContribution { isSticky?: boolean; sampleRequest?: string; commands?: IRawChatCommandContribution[]; - defaultImplicitVariables?: string[]; locations?: RawChatParticipantLocation[]; disambiguation?: { category: string; categoryName?: string /** Deprecated */; description: string; examples: string[] }[]; } diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts index de36f417e6a..e78ef045c61 100644 --- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts @@ -3,19 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { IJSONSchema } from '../../../../../base/common/jsonSchema.js'; -import { DisposableMap } from '../../../../../base/common/lifecycle.js'; +import { DisposableMap, Disposable } from '../../../../../base/common/lifecycle.js'; import { joinPath } from '../../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { localize } from '../../../../../nls.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; -import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js'; +import { ExtensionIdentifier, IExtensionManifest } from '../../../../../platform/extensions/common/extensions.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { ILanguageModelToolsService, IToolData } from '../languageModelToolsService.js'; import * as extensionsRegistry from '../../../../services/extensions/common/extensionsRegistry.js'; import { toolsParametersSchemaSchemaId } from './languageModelToolsParametersSchema.js'; +import { MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js'; +import { Registry } from '../../../../../platform/registry/common/platform.js'; +import { IExtensionFeatureTableRenderer, IRenderedData, ITableData, IRowData, IExtensionFeaturesRegistry, Extensions } from '../../../../services/extensionManagement/common/extensionFeatures.js'; export interface IRawToolContribution { name: string; @@ -192,3 +195,49 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri }); } } + +class LanguageModelToolDataRenderer extends Disposable implements IExtensionFeatureTableRenderer { + readonly type = 'table'; + + shouldRender(manifest: IExtensionManifest): boolean { + return !!manifest.contributes?.languageModelTools; + } + + render(manifest: IExtensionManifest): IRenderedData { + const contribs = manifest.contributes?.languageModelTools ?? []; + if (!contribs.length) { + return { data: { headers: [], rows: [] }, dispose: () => { } }; + } + + const headers = [ + localize('toolTableName', "Name"), + localize('toolTableDisplayName', "Display Name"), + localize('toolTableDescription', "Description"), + ]; + + const rows: IRowData[][] = contribs.map(t => { + return [ + new MarkdownString(`\`${t.name}\``), + t.displayName, + t.userDescription ?? t.modelDescription, + ]; + }); + + return { + data: { + headers, + rows + }, + dispose: () => { } + }; + } +} + +Registry.as(Extensions.ExtensionFeaturesRegistry).registerExtensionFeature({ + id: 'languageModelTools', + label: localize('langModelTools', "Language Model Tools"), + access: { + canToggle: false + }, + renderer: new SyncDescriptor(LanguageModelToolDataRenderer), +}); From 37b1016c80c5e6cd667c382765651055ad9f0ba8 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 18 Dec 2024 15:04:42 -0800 Subject: [PATCH 377/479] Lock some SCM strings (#236531) Fixes https://github.com/microsoft/vscode/issues/236530 --- extensions/git/package.nls.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index d706b650c8a..6db253137e1 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -310,6 +310,8 @@ "message": "[Download Git for Windows](https://git-scm.com/download/win)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", + "{Locked='](command:git.showOutput'}", + "{Locked='](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] @@ -318,6 +320,8 @@ "message": "[Download Git for macOS](https://git-scm.com/download/mac)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", + "{Locked='](command:git.showOutput'}", + "{Locked='](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] @@ -326,11 +330,19 @@ "message": "Source control depends on Git being installed.\n[Download Git for Linux](https://git-scm.com/download/linux)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", + "{Locked='](command:git.showOutput'}", + "{Locked='](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] }, - "view.workbench.scm.missing": "Install Git, a popular source control system, to track code changes and collaborate with others. Learn more in our [Git guides](https://aka.ms/vscode-scm).", + "view.workbench.scm.missing": { + "message": "Install Git, a popular source control system, to track code changes and collaborate with others. Learn more in our [Git guides](https://aka.ms/vscode-scm).", + "comment": [ + "{Locked='](https://aka.ms/vscode-scm'}", + "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" + ] + }, "view.workbench.scm.disabled": { "message": "If you would like to use Git features, please enable Git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "comment": [ From 03e9e31965788888ef75b7b5b5baa73897f2db0a Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 18 Dec 2024 17:25:00 -0800 Subject: [PATCH 378/479] Await remote extensions before checking enablement (#236538) await remote extensions before checking enablement --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index aab4103ddac..f42fd26ada5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -60,6 +60,7 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { URI } from '../../../../base/common/uri.js'; import { IHostService } from '../../../services/host/browser/host.js'; import Severity from '../../../../base/common/severity.js'; +import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -1022,6 +1023,7 @@ class ChatSetupContext extends Disposable { @IExtensionService private readonly extensionService: IExtensionService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, + @IRemoteExtensionsScannerService private readonly remoteExtensionsScannerService: IRemoteExtensionsScannerService, @ILogService private readonly logService: ILogService ) { super(); @@ -1047,6 +1049,7 @@ class ChatSetupContext extends Disposable { } })); + await this.remoteExtensionsScannerService.whenExtensionsReady(); const extensions = await this.extensionManagementService.getInstalled(); const defaultChatExtension = extensions.find(value => ExtensionIdentifier.equals(value.identifier.id, defaultChat.extensionId)); this.update({ installed: !!defaultChatExtension && this.extensionEnablementService.isEnabled(defaultChatExtension) }); From dd86be1a95485a849a2003721b6e08235c46b031 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 18 Dec 2024 17:33:45 -0800 Subject: [PATCH 379/479] lists: support user selection in lists, and adopt it in the debug console (#236534) - Adds a `userSelection` option on lists that can be used to control behavior - Uses the DND logic for handling scrolling near the top and bottom - Preserved selected list elements while they're selected to ensure they can be accurately copied. - The DOM events we get around selection are pretty poor. I support mouse here but I'm unclear if/how touch events should be handled. ![](https://memes.peet.io/img/24-12-f06c680d-f209-476b-8945-b6fc33efe502.mp4) Fixes #228432, cc @joaomoreno --- src/vs/base/browser/ui/list/listView.ts | 98 +++++++++++++++++-- src/vs/base/browser/ui/list/listWidget.ts | 1 + .../workbench/contrib/debug/browser/repl.ts | 1 + 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 00a19352e91..36355a377ba 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DataTransfers, IDragAndDropData } from '../../dnd.js'; -import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getTopLeftOffset, getWindow, isAncestor, isHTMLElement, isSVGElement, scheduleAtNextAnimationFrame } from '../../dom.js'; +import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getDocument, getTopLeftOffset, getWindow, isAncestor, isHTMLElement, isSVGElement, scheduleAtNextAnimationFrame } from '../../dom.js'; import { DomEmitter } from '../../event.js'; import { IMouseWheelEvent } from '../../mouseEvent.js'; import { EventType as TouchEventType, Gesture, GestureEvent } from '../../touch.js'; @@ -82,6 +82,7 @@ export interface IListViewOptions extends IListViewOptionsUpdate { readonly setRowHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; + readonly userSelection?: boolean; readonly accessibilityProvider?: IListViewAccessibilityProvider; readonly transformOptimization?: boolean; readonly alwaysConsumeMouseWheel?: boolean; @@ -105,7 +106,7 @@ const DefaultOptions = { horizontalScrolling: false, transformOptimization: true, alwaysConsumeMouseWheel: true, -}; +} satisfies IListViewOptions; export class ElementsDragAndDropData implements IDragAndDropData { @@ -321,6 +322,8 @@ export class ListView implements IListView { private currentDragFeedbackPosition: ListDragOverEffectPosition | undefined; private currentDragFeedbackDisposable: IDisposable = Disposable.None; private onDragLeaveTimeout: IDisposable = Disposable.None; + private currentSelectionDisposable: IDisposable = Disposable.None; + private currentSelectionBounds: IRange | undefined; private readonly disposables: DisposableStore = new DisposableStore(); @@ -369,7 +372,7 @@ export class ListView implements IListView { container: HTMLElement, private virtualDelegate: IListVirtualDelegate, renderers: IListRenderer[], - options: IListViewOptions = DefaultOptions as IListViewOptions + options: IListViewOptions = DefaultOptions ) { if (options.horizontalScrolling && options.supportDynamicHeights) { throw new Error('Horizontal scrolling and dynamic heights not supported simultaneously'); @@ -444,6 +447,12 @@ export class ListView implements IListView { this.disposables.add(addDisposableListener(this.domNode, 'drop', e => this.onDrop(this.toDragEvent(e)))); this.disposables.add(addDisposableListener(this.domNode, 'dragleave', e => this.onDragLeave(this.toDragEvent(e)))); this.disposables.add(addDisposableListener(this.domNode, 'dragend', e => this.onDragEnd(e))); + if (options.userSelection) { + if (options.dnd) { + throw new Error('DND and user selection cannot be used simultaneously'); + } + this.disposables.add(addDisposableListener(this.domNode, 'mousedown', e => this.onPotentialSelectionStart(e))); + } this.setRowLineHeight = options.setRowLineHeight ?? DefaultOptions.setRowLineHeight; this.setRowHeight = options.setRowHeight ?? DefaultOptions.setRowHeight; @@ -768,7 +777,7 @@ export class ListView implements IListView { } get firstVisibleIndex(): number { - const range = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + const range = this.getVisibleRange(this.lastRenderTop, this.lastRenderHeight); return range.start; } @@ -1168,6 +1177,73 @@ export class ListView implements IListView { this.dnd.onDragStart?.(this.currentDragData, event); } + private onPotentialSelectionStart(e: MouseEvent) { + this.currentSelectionDisposable.dispose(); + const doc = getDocument(this.domNode); + + // Set up both the 'movement store' for watching the mouse, and the + // 'selection store' which lasts as long as there's a selection, even + // after the usr has stopped modifying it. + const selectionStore = this.currentSelectionDisposable = new DisposableStore(); + const movementStore = selectionStore.add(new DisposableStore()); + + // The selection events we get from the DOM are fairly limited and we lack a 'selection end' event. + // Selection events also don't tell us where the input doing the selection is. So, make a poor + // assumption that a user is using the mouse, and base our events on that. + movementStore.add(addDisposableListener(this.domNode, 'selectstart', () => { + this.setupDragAndDropScrollTopAnimation(e); + + movementStore.add(addDisposableListener(doc, 'mousemove', e => this.setupDragAndDropScrollTopAnimation(e))); + + // The selection is cleared either on mouseup if there's no selection, or on next mousedown + // when `this.currentSelectionDisposable` is reset. + selectionStore.add(toDisposable(() => { + const previousRenderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); + this.currentSelectionBounds = undefined; + this.render(previousRenderRange, this.lastRenderTop, this.lastRenderHeight, undefined, undefined); + })); + selectionStore.add(addDisposableListener(doc, 'selectionchange', () => { + const selection = doc.getSelection(); + if (!selection) { + return; + } + + let start = this.getIndexOfListElement(selection.anchorNode as HTMLElement); + let end = this.getIndexOfListElement(selection.focusNode as HTMLElement); + if (start !== undefined && end !== undefined) { + if (end < start) { + [start, end] = [end, start]; + } + this.currentSelectionBounds = { start, end }; + } + })); + })); + + movementStore.add(addDisposableListener(doc, 'mouseup', () => { + movementStore.dispose(); + + if (doc.getSelection()?.isCollapsed !== false) { + selectionStore.dispose(); + } + })); + } + + private getIndexOfListElement(element: HTMLElement | null): number | undefined { + if (!element || !this.domNode.contains(element)) { + return undefined; + } + + while (element && element !== this.domNode) { + if (element.dataset?.index) { + return Number(element.dataset.index); + } + + element = element.parentElement; + } + + return undefined; + } + private onDragOver(event: IListDragEvent): boolean { event.browserEvent.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) @@ -1327,7 +1403,7 @@ export class ListView implements IListView { // DND scroll top animation - private setupDragAndDropScrollTopAnimation(event: DragEvent): void { + private setupDragAndDropScrollTopAnimation(event: DragEvent | MouseEvent): void { if (!this.dragOverAnimationDisposable) { const viewTop = getTopLeftOffset(this.domNode).top; this.dragOverAnimationDisposable = animate(getWindow(this.domNode), this.animateDragAndDropScrollTop.bind(this, viewTop)); @@ -1401,13 +1477,23 @@ export class ListView implements IListView { return undefined; } - protected getRenderRange(renderTop: number, renderHeight: number): IRange { + private getVisibleRange(renderTop: number, renderHeight: number): IRange { return { start: this.rangeMap.indexAt(renderTop), end: this.rangeMap.indexAfter(renderTop + renderHeight - 1) }; } + protected getRenderRange(renderTop: number, renderHeight: number): IRange { + const range = this.getVisibleRange(renderTop, renderHeight); + if (this.currentSelectionBounds) { + range.start = Math.min(range.start, this.currentSelectionBounds.start); + range.end = Math.max(range.end, this.currentSelectionBounds.end + 1); + } + + return range; + } + /** * Given a stable rendered state, checks every rendered element whether it needs * to be probed for dynamic height. Adjusts scroll height and top if necessary. diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index ae9d5a46fb4..78932044f96 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1056,6 +1056,7 @@ export interface IListOptions extends IListOptionsUpdate { readonly setRowHeight?: boolean; readonly supportDynamicHeights?: boolean; readonly mouseSupport?: boolean; + readonly userSelection?: boolean; readonly horizontalScrolling?: boolean; readonly scrollByPage?: boolean; readonly transformOptimization?: boolean; diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 4bcfe48be3e..a9959c5d1c5 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -667,6 +667,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { filter: this.filter, accessibilityProvider: new ReplAccessibilityProvider(), identityProvider, + userSelection: true, mouseSupport: false, findWidgetEnabled: true, keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IReplElement) => e.toString(true) }, From acd32b17b837b05a64275c297949753df46dbe6d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 18 Dec 2024 20:17:02 -0800 Subject: [PATCH 380/479] Use IExtensionsWorkbenchService instead (#236540) Use IExtensionsWorkbenchService --- src/vs/workbench/contrib/chat/browser/chatSetup.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index f42fd26ada5..a325d5526f1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -26,7 +26,6 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../platform/log/common/log.js'; @@ -60,7 +59,6 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { URI } from '../../../../base/common/uri.js'; import { IHostService } from '../../../services/host/browser/host.js'; import Severity from '../../../../base/common/severity.js'; -import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -1021,10 +1019,9 @@ class ChatSetupContext extends Disposable { @IStorageService private readonly storageService: IStorageService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IExtensionService private readonly extensionService: IExtensionService, - @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @IRemoteExtensionsScannerService private readonly remoteExtensionsScannerService: IRemoteExtensionsScannerService, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, ) { super(); @@ -1049,10 +1046,9 @@ class ChatSetupContext extends Disposable { } })); - await this.remoteExtensionsScannerService.whenExtensionsReady(); - const extensions = await this.extensionManagementService.getInstalled(); + const extensions = await this.extensionsWorkbenchService.queryLocal(); const defaultChatExtension = extensions.find(value => ExtensionIdentifier.equals(value.identifier.id, defaultChat.extensionId)); - this.update({ installed: !!defaultChatExtension && this.extensionEnablementService.isEnabled(defaultChatExtension) }); + this.update({ installed: !!defaultChatExtension?.local && this.extensionEnablementService.isEnabled(defaultChatExtension.local) }); } update(context: { installed: boolean }): Promise; From d47f63e5dcde69292e29018bd6fbb4bc93ccaaf1 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:35:34 +0100 Subject: [PATCH 381/479] Revert focus behavior fix for views/panels (#236556) Revert "Fix inconsistent focus behavior when toggling views/panels (#235622)" This reverts commit d5746c5593f6afe15e44a9f7877a064df6605d11. --- src/vs/workbench/browser/layout.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 592bb4146af..bf1f54199d1 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -1783,9 +1783,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi else if (!hidden && !this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)) { const viewletToOpen = this.paneCompositeService.getLastActivePaneCompositeId(ViewContainerLocation.Sidebar); if (viewletToOpen) { - const viewlet = this.paneCompositeService.openPaneComposite(viewletToOpen, ViewContainerLocation.Sidebar); + const viewlet = this.paneCompositeService.openPaneComposite(viewletToOpen, ViewContainerLocation.Sidebar, true); if (!viewlet) { - this.paneCompositeService.openPaneComposite(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id, ViewContainerLocation.Sidebar); + this.paneCompositeService.openPaneComposite(this.viewDescriptorService.getDefaultViewContainer(ViewContainerLocation.Sidebar)?.id, ViewContainerLocation.Sidebar, true); } } } @@ -1931,7 +1931,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (panelToOpen) { - this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.Panel); + const focus = !skipLayout; + this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.Panel, focus); } } @@ -2030,7 +2031,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (panelToOpen) { - this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.AuxiliaryBar); + const focus = !skipLayout; + this.paneCompositeService.openPaneComposite(panelToOpen, ViewContainerLocation.AuxiliaryBar, focus); } } From d74499bdb99fd78fb4c6ef819e9c76fa766cae1e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 19 Dec 2024 08:23:06 +0100 Subject: [PATCH 382/479] @vscode/proxy-agent 0.28.0 --- package-lock.json | 8 ++++---- package.json | 2 +- remote/package-lock.json | 8 ++++---- remote/package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 60dc545ca37..5f2b14c3e39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.8", - "@vscode/proxy-agent": "^0.27.0", + "@vscode/proxy-agent": "^0.28.0", "@vscode/ripgrep": "^1.15.9", "@vscode/spdlog": "^0.15.0", "@vscode/sqlite3": "5.1.8-vscode", @@ -2848,9 +2848,9 @@ } }, "node_modules/@vscode/proxy-agent": { - "version": "0.27.0", - "resolved": "git+ssh://git@github.com/microsoft/vscode-proxy-agent.git#0803e0a7d1249bcb64ae9d256d0ee732b7246ea5", - "integrity": "sha512-ID1MOlynRkVN121n85Hs1enjW16B5Z1O6bMDkSPqvAMVD6eHUOqmgPZuEkBbRI43PfA6boUEY9Ndjp+0wPKtsg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.28.0.tgz", + "integrity": "sha512-7rYF8ju0dP/ASpjjnuOCvzRosGLoKz0WOyNohREUskRdrvMEnYuEUXy84lHlH+4+MD8CZZjw2SUzhjHaJK1hxg==", "license": "MIT", "dependencies": { "@tootallnate/once": "^3.0.0", diff --git a/package.json b/package.json index 8dd5e0a86a3..3316ae225ea 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.8", - "@vscode/proxy-agent": "^0.27.0", + "@vscode/proxy-agent": "^0.28.0", "@vscode/ripgrep": "^1.15.9", "@vscode/spdlog": "^0.15.0", "@vscode/sqlite3": "5.1.8-vscode", diff --git a/remote/package-lock.json b/remote/package-lock.json index dfbff101a88..3cf22939861 100644 --- a/remote/package-lock.json +++ b/remote/package-lock.json @@ -13,7 +13,7 @@ "@parcel/watcher": "2.5.0", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.27.0", + "@vscode/proxy-agent": "^0.28.0", "@vscode/ripgrep": "^1.15.9", "@vscode/spdlog": "^0.15.0", "@vscode/tree-sitter-wasm": "^0.0.4", @@ -418,9 +418,9 @@ "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==" }, "node_modules/@vscode/proxy-agent": { - "version": "0.27.0", - "resolved": "git+ssh://git@github.com/microsoft/vscode-proxy-agent.git#0803e0a7d1249bcb64ae9d256d0ee732b7246ea5", - "integrity": "sha512-ID1MOlynRkVN121n85Hs1enjW16B5Z1O6bMDkSPqvAMVD6eHUOqmgPZuEkBbRI43PfA6boUEY9Ndjp+0wPKtsg==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.28.0.tgz", + "integrity": "sha512-7rYF8ju0dP/ASpjjnuOCvzRosGLoKz0WOyNohREUskRdrvMEnYuEUXy84lHlH+4+MD8CZZjw2SUzhjHaJK1hxg==", "license": "MIT", "dependencies": { "@tootallnate/once": "^3.0.0", diff --git a/remote/package.json b/remote/package.json index b5ebb761e09..5a61a15c051 100644 --- a/remote/package.json +++ b/remote/package.json @@ -8,7 +8,7 @@ "@parcel/watcher": "2.5.0", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.27.0", + "@vscode/proxy-agent": "^0.28.0", "@vscode/ripgrep": "^1.15.9", "@vscode/spdlog": "^0.15.0", "@vscode/tree-sitter-wasm": "^0.0.4", From 7e000daa484bbf6e434c9942c39bb2eee569619c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 19 Dec 2024 10:55:10 +0100 Subject: [PATCH 383/479] recovery fix: fixing installing extensions everywhere when it is already installed locally (#236562) * recovery fix: fixing installing extensions everywhere when it is already installed locally * clean up --- .../extensions/browser/extensionsWorkbenchService.ts | 8 +++++--- .../common/extensionManagementService.ts | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index ae7df7d09b3..0967e7d34c7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -2368,8 +2368,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (extension?.isMalicious) { throw new Error(nls.localize('malicious', "This extension is reported to be problematic.")); } + // TODO: @sandy081 - Install the extension only on servers where it is not installed // Do not install if requested to enable and extension is already installed - if (!(installOptions.enable && extension?.local)) { + if (installOptions.installEverywhere || !(installOptions.enable && extension?.local)) { if (!installable) { if (!gallery) { const id = isString(arg) ? arg : (arg).identifier.id; @@ -2730,10 +2731,11 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return this.extensionManagementService.installVSIX(vsix, manifest, installOptions); } - private installFromGallery(extension: IExtension, gallery: IGalleryExtension, installOptions?: InstallOptions): Promise { + private installFromGallery(extension: IExtension, gallery: IGalleryExtension, installOptions?: InstallExtensionOptions): Promise { installOptions = installOptions ?? {}; installOptions.pinned = extension.local?.pinned || !this.shouldAutoUpdateExtension(extension); - if (extension.local) { + // TODO: @sandy081 - Install the extension only on servers where it is not installed + if (!installOptions.installEverywhere && extension.local) { installOptions.productVersion = this.getProductVersion(); installOptions.operation = InstallOperation.Update; return this.extensionManagementService.updateFromGallery(gallery, extension.local, installOptions); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index d8236828324..bc0a4687128 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -466,7 +466,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench installOptions = { ...(installOptions || {}), isMachineScoped }; } - if (installOptions.installEverywhere || (!installOptions.isMachineScoped && this.isExtensionsSyncEnabled())) { + if (!installOptions.isMachineScoped && this.isExtensionsSyncEnabled()) { if (this.extensionManagementServerService.localExtensionManagementServer && !servers.includes(this.extensionManagementServerService.localExtensionManagementServer) && await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.canInstall(gallery) === true) { @@ -597,7 +597,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench } } - private async validateAndGetExtensionManagementServersToInstall(gallery: IGalleryExtension, installOptions?: InstallOptions): Promise { + private async validateAndGetExtensionManagementServersToInstall(gallery: IGalleryExtension, installOptions?: IWorkbenchInstallOptions): Promise { const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); if (!manifest) { @@ -606,8 +606,8 @@ export class ExtensionManagementService extends Disposable implements IWorkbench const servers: IExtensionManagementServer[] = []; - // Install Language pack on local and remote servers - if (isLanguagePackExtension(manifest)) { + // Install everywhere if asked to install everywhere or if the extension is a language pack + if (installOptions?.installEverywhere || isLanguagePackExtension(manifest)) { servers.push(...this.servers.filter(server => server !== this.extensionManagementServerService.webExtensionManagementServer)); } else { const server = this.getExtensionManagementServerToInstall(manifest); From 7efdaa5e8eadfdb97428e86c0ade801d22ab5868 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 19 Dec 2024 11:34:50 +0100 Subject: [PATCH 384/479] don't show inline chat hint when line has too many comments or strings. (#236567) The limit is 25% strings, comments, or regex token and (as before) lines ending in comments https://github.com/microsoft/vscode-copilot-release/issues/3009 --- .../browser/inlineChatCurrentLine.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts index 43e73454efd..08c9911fdb8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts @@ -140,14 +140,32 @@ export class ShowInlineChatHintAction extends EditorAction2 { model.tokenization.forceTokenization(position.lineNumber); const tokens = model.tokenization.getLineTokens(position.lineNumber); - const tokenIndex = tokens.findTokenIndexAtOffset(position.column - 1); - const tokenType = tokens.getStandardTokenType(tokenIndex); - if (tokenType === StandardTokenType.Comment) { + let totalLength = 0; + let specialLength = 0; + let lastTokenType: StandardTokenType | undefined; + + tokens.forEach(idx => { + const tokenType = tokens.getStandardTokenType(idx); + const startOffset = tokens.getStartOffset(idx); + const endOffset = tokens.getEndOffset(idx); + totalLength += endOffset - startOffset; + + if (tokenType !== StandardTokenType.Other) { + specialLength += endOffset - startOffset; + } + lastTokenType = tokenType; + }); + + if (specialLength / totalLength > 0.25) { ctrl.hide(); - } else { - ctrl.show(); + return; + } + if (lastTokenType === StandardTokenType.Comment) { + ctrl.hide(); + return; } + ctrl.show(); } } From cbfd8ab51359eafff1b5258f25dc334b2d9e26e5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 19 Dec 2024 02:50:00 -0800 Subject: [PATCH 385/479] Cache builtin commands and get all global commands for pwsh This should make things quite a bit faster for expensive fetching of commands. This also caches empty builtin commands so it's not attempted every time. Fixes #236097 Part of #235024 --- .../src/terminalSuggestMain.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 4acd5b59f8a..13ff032037a 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -12,14 +12,14 @@ import codeCompletionSpec from './completions/code'; import cdSpec from './completions/cd'; let cachedAvailableCommands: Set | undefined; -let cachedBuiltinCommands: Map | undefined; +const cachedBuiltinCommands: Map = new Map(); export const availableSpecs = [codeCompletionSpec, codeInsidersCompletionSpec, cdSpec]; function getBuiltinCommands(shell: string): string[] | undefined { try { const shellType = path.basename(shell, path.extname(shell)); - const cachedCommands = cachedBuiltinCommands?.get(shellType); + const cachedCommands = cachedBuiltinCommands.get(shellType); if (cachedCommands) { return cachedCommands; } @@ -38,14 +38,14 @@ function getBuiltinCommands(shell: string): string[] | undefined { break; } case 'fish': { - // TODO: ghost text in the command line prevents - // completions from working ATM for fish + // TODO: Ghost text in the command line prevents completions from working ATM for fish const fishOutput = execSync('functions -n', options); commands = fishOutput.split(', ').filter(filter); break; } case 'pwsh': { - const output = execSync('Get-Command | Select-Object Name, CommandType, DisplayName | ConvertTo-Json', options); + // TODO: Select `CommandType, DisplayName` and map to a rich type with kind and detail + const output = execSync('Get-Command -All | Select-Object Name | ConvertTo-Json', options); let json: any; try { json = JSON.parse(output); @@ -53,17 +53,12 @@ function getBuiltinCommands(shell: string): string[] | undefined { console.error('Error parsing pwsh output:', e); return []; } - // TODO: Return a rich type with kind and detail commands = (json as any[]).map(e => e.Name); break; } } - // TODO: Cache failure results too - if (commands?.length) { - cachedBuiltinCommands?.set(shellType, commands); - return commands; - } - return; + cachedBuiltinCommands.set(shellType, commands); + return commands; } catch (error) { console.error('Error fetching builtin commands:', error); From 011e8ec6df9cfd2aef2da84a617d4489fb9f372a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 19 Dec 2024 11:56:01 +0100 Subject: [PATCH 386/479] chat setup - use 1 service as source of truth for extensions (#236564) --- .../contrib/chat/browser/chatSetup.ts | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index a325d5526f1..3e93dbf6375 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -43,7 +43,6 @@ import { IViewDescriptorService, ViewContainerLocation } from '../../../common/v import { IActivityService, ProgressBadge } from '../../../services/activity/common/activity.js'; import { AuthenticationSession, IAuthenticationExtensionsService, IAuthenticationService } from '../../../services/authentication/common/authentication.js'; import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js'; -import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser/layoutService.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; @@ -1018,7 +1017,6 @@ class ChatSetupContext extends Disposable { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IStorageService private readonly storageService: IStorageService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @ILogService private readonly logService: ILogService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -1030,25 +1028,19 @@ class ChatSetupContext extends Disposable { } private async checkExtensionInstallation(): Promise { - this._register(this.extensionService.onDidChangeExtensions(result => { - for (const extension of result.removed) { - if (ExtensionIdentifier.equals(defaultChat.extensionId, extension.identifier)) { - this.update({ installed: false }); - break; - } - } - for (const extension of result.added) { - if (ExtensionIdentifier.equals(defaultChat.extensionId, extension.identifier)) { - this.update({ installed: true }); - break; - } + // Await extensions to be ready to be queries + await this.extensionsWorkbenchService.queryLocal(); + + // Listen to change and process extensions once + this._register(Event.runAndSubscribe(this.extensionsWorkbenchService.onChange, (e) => { + if (e && !ExtensionIdentifier.equals(e.identifier.id, defaultChat.extensionId)) { + return; // unrelated event } - })); - const extensions = await this.extensionsWorkbenchService.queryLocal(); - const defaultChatExtension = extensions.find(value => ExtensionIdentifier.equals(value.identifier.id, defaultChat.extensionId)); - this.update({ installed: !!defaultChatExtension?.local && this.extensionEnablementService.isEnabled(defaultChatExtension.local) }); + const defaultChatExtension = this.extensionsWorkbenchService.local.find(value => ExtensionIdentifier.equals(value.identifier.id, defaultChat.extensionId)); + this.update({ installed: !!defaultChatExtension?.local && this.extensionEnablementService.isEnabled(defaultChatExtension.local) }); + })); } update(context: { installed: boolean }): Promise; From 7f512c528d36154fe0cab63dcd75b505013fff44 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 19 Dec 2024 12:13:57 +0100 Subject: [PATCH 387/479] chat - fix setup in web based on remote connection --- .../chat/browser/actions/chatActions.ts | 19 ++++++++++++++----- .../contrib/chat/browser/chat.contribution.ts | 2 ++ .../contrib/chat/browser/chatSetup.ts | 10 ++++++++-- .../contrib/chat/common/chatContextKeys.ts | 4 ++++ .../electron-sandbox/chat.contribution.ts | 2 -- .../common/gettingStartedContent.ts | 7 +++---- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index a0bb888eb80..d1d7d951559 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -9,7 +9,7 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { fromNowByDay } from '../../../../../base/common/date.js'; import { Event } from '../../../../../base/common/event.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; -import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, markAsSingleton } from '../../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; @@ -531,7 +531,10 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, { submenu: MenuId.ChatCommandCenter, title: localize('title4', "Chat"), icon: Codicon.copilot, - when: ContextKeyExpr.has('config.chat.commandCenter.enabled'), + when: ContextKeyExpr.and( + ChatContextKeys.supported, + ContextKeyExpr.has('config.chat.commandCenter.enabled') + ), order: 10001, }); @@ -541,7 +544,10 @@ registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction { 'chat.commandCenter.enabled', localize('toggle.chatControl', 'Copilot Controls'), localize('toggle.chatControlsDescription', "Toggle visibility of the Copilot Controls in title bar"), 4, false, - ContextKeyExpr.has('config.window.commandCenter') + ContextKeyExpr.and( + ChatContextKeys.supported, + ContextKeyExpr.has('config.window.commandCenter') + ) ); } }); @@ -561,7 +567,7 @@ export class ChatCommandCenterRendering extends Disposable implements IWorkbench const contextKeySet = new Set([ChatContextKeys.Setup.signedOut.key]); - this._store.add(actionViewItemService.register(MenuId.CommandCenter, MenuId.ChatCommandCenter, (action, options) => { + const disposable = actionViewItemService.register(MenuId.CommandCenter, MenuId.ChatCommandCenter, (action, options) => { if (!(action instanceof SubmenuItemAction)) { return undefined; } @@ -607,6 +613,9 @@ export class ChatCommandCenterRendering extends Disposable implements IWorkbench agentService.onDidChangeAgents, chatQuotasService.onDidChangeQuotas, Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(contextKeySet)) - ))); + )); + + // Reduces flicker a bit on reload/restart + markAsSingleton(disposable); } } diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index b03d4605d16..456c8817cd4 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -81,6 +81,7 @@ import { Extensions, IConfigurationMigrationRegistry } from '../../../common/con import { ChatEditorOverlayController } from './chatEditorOverlay.js'; import { ChatRelatedFilesContribution } from './contrib/chatInputRelatedFilesContrib.js'; import { ChatQuotasService, ChatQuotasStatusBarEntry, IChatQuotasService } from './chatQuotasService.js'; +import { ChatSetupContribution } from './chatSetup.js'; // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -316,6 +317,7 @@ registerWorkbenchContribution2(ChatEditorSaving.ID, ChatEditorSaving, WorkbenchP registerWorkbenchContribution2(ChatEditorAutoSaveDisabler.ID, ChatEditorAutoSaveDisabler, WorkbenchPhase.BlockRestore); registerWorkbenchContribution2(ChatViewsWelcomeHandler.ID, ChatViewsWelcomeHandler, WorkbenchPhase.BlockStartup); registerWorkbenchContribution2(ChatGettingStartedContribution.ID, ChatGettingStartedContribution, WorkbenchPhase.Eventually); +registerWorkbenchContribution2(ChatSetupContribution.ID, ChatSetupContribution, WorkbenchPhase.BlockRestore); registerWorkbenchContribution2(ChatQuotasStatusBarEntry.ID, ChatQuotasStatusBarEntry, WorkbenchPhase.Eventually); registerChatActions(); diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 3e93dbf6375..53a86ec5245 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -58,6 +58,8 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { URI } from '../../../../base/common/uri.js'; import { IHostService } from '../../../services/host/browser/host.js'; import Severity from '../../../../base/common/severity.js'; +import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { isWeb } from '../../../../base/common/platform.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -106,11 +108,15 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr constructor( @IProductService private readonly productService: IProductService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { super(); - if (!this.productService.defaultChatAgent) { + if ( + !this.productService.defaultChatAgent || // needs product config + (isWeb && !this.environmentService.remoteAuthority) // only enabled locally or a remote backend + ) { return; } diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index f615233de50..be03126dd2d 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -5,6 +5,8 @@ import { localize } from '../../../../nls.js'; import { ContextKeyExpr, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js'; +import { RemoteNameContext } from '../../../common/contextkeys.js'; import { ChatAgentLocation } from './chatAgents.js'; export namespace ChatContextKeys { @@ -27,7 +29,9 @@ export namespace ChatContextKeys { export const inChatInput = new RawContextKey('inChatInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the chat input, false otherwise.") }); export const inChatSession = new RawContextKey('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") }); + export const supported = ContextKeyExpr.or(IsWebContext.toNegated(), RemoteNameContext.notEqualsTo('')); // supported on desktop and in web only with a remote connection export const enabled = new RawContextKey('chatIsEnabled', false, { type: 'boolean', description: localize('chatIsEnabled', "True when chat is enabled because a default chat participant is activated with an implementation.") }); + export const panelParticipantRegistered = new RawContextKey('chatPanelParticipantRegistered', false, { type: 'boolean', description: localize('chatParticipantRegistered', "True when a default chat participant is registered for the panel.") }); export const editingParticipantRegistered = new RawContextKey('chatEditingParticipantRegistered', false, { type: 'boolean', description: localize('chatEditingParticipantRegistered', "True when a default chat participant is registered for editing.") }); export const chatEditingCanUndo = new RawContextKey('chatEditingCanUndo', false, { type: 'boolean', description: localize('chatEditingCanUndo', "True when it is possible to undo an interaction in the editing panel.") }); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts index e71f4342b15..0e888b7f864 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -6,7 +6,6 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction, KeywordActivationContribution, InstallSpeechProviderForVoiceChatAction, HoldToVoiceChatInChatViewAction, ReadChatResponseAloud, StopReadAloud, StopReadChatItemAloud } from './actions/voiceChatActions.js'; import { registerAction2 } from '../../../../platform/actions/common/actions.js'; import { WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; -import { ChatSetupContribution } from '../browser/chatSetup.js'; registerAction2(StartVoiceChatAction); registerAction2(InstallSpeechProviderForVoiceChatAction); @@ -24,4 +23,3 @@ registerAction2(StopReadChatItemAloud); registerAction2(StopReadAloud); registerWorkbenchContribution2(KeywordActivationContribution.ID, KeywordActivationContribution, WorkbenchPhase.AfterRestored); -registerWorkbenchContribution2(ChatSetupContribution.ID, ChatSetupContribution, WorkbenchPhase.BlockRestore); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index 7fd3e30163f..53c8ae80511 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -282,7 +282,6 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ // id: 'settings', // title: localize('gettingStarted.settings.title', "Tune your settings"), // description: localize('gettingStarted.settings.description.interpolated', "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", Button(localize('tweakSettings', "Open Settings"), 'command:toSide:workbench.action.openSettings')), - // when: '!config.chat.experimental.offerSetup', // media: { // type: 'svg', altText: 'VS Code Settings', path: 'settings.svg' // }, @@ -291,7 +290,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ // id: 'settingsSync', // title: localize('gettingStarted.settingsSync.title', "Sync settings across devices"), // description: localize('gettingStarted.settingsSync.description.interpolated', "Keep your essential customizations backed up and updated across all your devices.\n{0}", Button(localize('enableSync', "Backup and Sync Settings"), 'command:workbench.userDataSync.actions.turnOn')), - // when: '!config.chat.experimental.offerSetup && syncStatus != uninitialized', + // when: 'syncStatus != uninitialized', // completionEvents: ['onEvent:sync-enabled'], // media: { // type: 'svg', altText: 'The "Turn on Sync" entry in the settings gear menu.', path: 'settingsSync.svg' @@ -318,7 +317,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ // id: 'pickAFolderTask-Mac', // title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"), // description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFileFolder')), - // when: '!config.chat.experimental.offerSetup && isMac && workspaceFolderCount == 0', + // when: 'isMac && workspaceFolderCount == 0', // media: { // type: 'svg', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: 'openFolder.svg' // } @@ -327,7 +326,7 @@ export const walkthroughs: GettingStartedWalkthroughContent = [ // id: 'pickAFolderTask-Other', // title: localize('gettingStarted.setup.OpenFolder.title', "Open up your code"), // description: localize('gettingStarted.setup.OpenFolder.description.interpolated', "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", Button(localize('pickFolder', "Pick a Folder"), 'command:workbench.action.files.openFolder')), - // when: '!config.chat.experimental.offerSetup && !isMac && workspaceFolderCount == 0', + // when: '!isMac && workspaceFolderCount == 0', // media: { // type: 'svg', altText: 'Explorer view showing buttons for opening folder and cloning repository.', path: 'openFolder.svg' // } From 22959031c000aa613a72c6336dda2487e4d514a2 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:23:45 +0100 Subject: [PATCH 388/479] Enable dragging of editor from breadcrumbs in single tabs (#236571) drag editor from single tab breadcrumbs --- .../browser/parts/editor/breadcrumbsControl.ts | 16 +++++++++++----- .../browser/parts/editor/breadcrumbsModel.ts | 2 +- .../browser/parts/editor/editorTitleControl.ts | 3 ++- .../parts/editor/singleEditorTabsControl.ts | 3 ++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 51ca71f42d1..9a2581907c4 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -25,7 +25,7 @@ import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/c import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; -import { fillInSymbolsDragData } from '../../../../platform/dnd/browser/dnd.js'; +import { fillInSymbolsDragData, LocalSelectionTransfer } from '../../../../platform/dnd/browser/dnd.js'; import { FileKind, IFileService, IFileStat } from '../../../../platform/files/common/files.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { InstantiationService } from '../../../../platform/instantiation/common/instantiationService.js'; @@ -39,7 +39,7 @@ import { EditorResourceAccessor, IEditorPartOptions, SideBySideEditor } from '.. import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from '../../../services/editor/common/editorService.js'; import { IOutline } from '../../../services/outline/browser/outline.js'; -import { fillEditorsDragData } from '../../dnd.js'; +import { DraggedEditorIdentifier, fillEditorsDragData } from '../../dnd.js'; import { DEFAULT_LABELS_CONTAINER, ResourceLabels } from '../../labels.js'; import { BreadcrumbsConfig, IBreadcrumbsService } from './breadcrumbs.js'; import { BreadcrumbsModel, FileElement, OutlineElement2 } from './breadcrumbsModel.js'; @@ -105,7 +105,7 @@ class OutlineItem extends BreadcrumbsItem { this._disposables.add(toDisposable(() => { renderer.disposeTemplate(template); })); if (element instanceof OutlineElement && outline.uri) { - this._disposables.add(this._instantiationService.invokeFunction(accessor => createBreadcrumbDndObserver(accessor, container, element.symbol.name, { symbol: element.symbol, uri: outline.uri! }))); + this._disposables.add(this._instantiationService.invokeFunction(accessor => createBreadcrumbDndObserver(accessor, container, element.symbol.name, { symbol: element.symbol, uri: outline.uri! }, this.model, this.options.dragEditor))); } } } @@ -151,12 +151,12 @@ class FileItem extends BreadcrumbsItem { container.classList.add(FileKind[this.element.kind].toLowerCase()); this._disposables.add(label); - this._disposables.add(this._instantiationService.invokeFunction(accessor => createBreadcrumbDndObserver(accessor, container, basename(this.element.uri), this.element.uri))); + this._disposables.add(this._instantiationService.invokeFunction(accessor => createBreadcrumbDndObserver(accessor, container, basename(this.element.uri), this.element.uri, this.model, this.options.dragEditor))); } } -function createBreadcrumbDndObserver(accessor: ServicesAccessor, container: HTMLElement, label: string, item: URI | { symbol: DocumentSymbol; uri: URI }): IDisposable { +function createBreadcrumbDndObserver(accessor: ServicesAccessor, container: HTMLElement, label: string, item: URI | { symbol: DocumentSymbol; uri: URI }, model: BreadcrumbsModel, dragEditor: boolean): IDisposable { const instantiationService = accessor.get(IInstantiationService); container.draggable = true; @@ -183,6 +183,11 @@ function createBreadcrumbDndObserver(accessor: ServicesAccessor, container: HTML kind: item.symbol.kind }], event); } + + if (dragEditor && model.editor && model.editor?.input) { + const editorTransfer = LocalSelectionTransfer.getInstance(); + editorTransfer.setData([new DraggedEditorIdentifier({ editor: model.editor.input, groupId: model.editor.group.id })], DraggedEditorIdentifier.prototype); + } }); // Create drag image and remove when dropped @@ -209,6 +214,7 @@ export interface IBreadcrumbsControlOptions { readonly showSymbolIcons: boolean; readonly showDecorationColors: boolean; readonly showPlaceholder: boolean; + readonly dragEditor: boolean; readonly widgetStyles?: IBreadcrumbsWidgetStyles; } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index f49b2301ea5..1c4aaef090a 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -49,7 +49,7 @@ export class BreadcrumbsModel { constructor( readonly resource: URI, - editor: IEditorPane | undefined, + readonly editor: IEditorPane | undefined, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, @IOutlineService private readonly _outlineService: IOutlineService, diff --git a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts index ac74f9b7728..65ef9fca411 100644 --- a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts @@ -90,7 +90,8 @@ export class EditorTitleControl extends Themable { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, - showPlaceholder: true + showPlaceholder: true, + dragEditor: false, })); // Breadcrumbs enablement & visibility change have an impact on layout diff --git a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts index b8d31c0d171..a3b16b2d7fc 100644 --- a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts @@ -60,7 +60,8 @@ export class SingleEditorTabsControl extends EditorTabsControl { showSymbolIcons: true, showDecorationColors: false, widgetStyles: { ...defaultBreadcrumbsWidgetStyles, breadcrumbsBackground: Color.transparent.toString() }, - showPlaceholder: false + showPlaceholder: false, + dragEditor: true, })); this._register(this.breadcrumbsControlFactory.onDidEnablementChange(() => this.handleBreadcrumbsEnablementChange())); titleContainer.classList.toggle('breadcrumbs', Boolean(this.breadcrumbsControl)); From 1b078e1a20c122f11d4df60dd836f9ee2f87c2f6 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 19 Dec 2024 12:42:58 +0100 Subject: [PATCH 389/479] Disable word wrap in preview editor (#236574) --- .../browser/view/inlineEdits/sideBySideDiff.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 5f36bec5b33..b34dc44ac1d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -268,6 +268,8 @@ export class InlineEditsSideBySideDiff extends Disposable { }, readOnly: true, wordWrap: 'off', + wordWrapOverride1: 'off', + wordWrapOverride2: 'off', }, { contributions: [], }, this._editor From b392dd9337e935d639d6a7b18ae02164eda7c283 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 19 Dec 2024 12:51:27 +0100 Subject: [PATCH 390/479] simplification: tokenizeLineWithEdit -> tokenizeLinesAt (#236577) --- src/vs/editor/common/core/offsetEdit.ts | 12 ++++++ src/vs/editor/common/model/textModelTokens.ts | 38 ++++++------------- .../common/model/tokenizationTextModelPart.ts | 13 ++++--- src/vs/editor/common/model/tokens.ts | 4 +- .../editor/common/model/treeSitterTokens.ts | 5 +-- .../common/tokenizationTextModelPart.ts | 31 +-------------- src/vs/editor/common/tokens/lineTokens.ts | 4 ++ src/vs/editor/common/tokens/tokenArray.ts | 25 ++++++++++++ .../browser/model/provideInlineCompletions.ts | 18 ++++----- .../browser/view/ghostText/ghostTextView.ts | 11 ++---- 10 files changed, 77 insertions(+), 84 deletions(-) diff --git a/src/vs/editor/common/core/offsetEdit.ts b/src/vs/editor/common/core/offsetEdit.ts index 426f90d22b6..d6886ee5fae 100644 --- a/src/vs/editor/common/core/offsetEdit.ts +++ b/src/vs/editor/common/core/offsetEdit.ts @@ -228,6 +228,10 @@ export class SingleOffsetEdit { return new SingleOffsetEdit(OffsetRange.emptyAt(offset), text); } + public static replace(range: OffsetRange, text: string): SingleOffsetEdit { + return new SingleOffsetEdit(range, text); + } + constructor( public readonly replaceRange: OffsetRange, public readonly newText: string, @@ -240,6 +244,14 @@ export class SingleOffsetEdit { get isEmpty() { return this.newText.length === 0 && this.replaceRange.length === 0; } + + apply(str: string): string { + return str.substring(0, this.replaceRange.start) + this.newText + str.substring(this.replaceRange.endExclusive); + } + + getRangeAfterApply(): OffsetRange { + return new OffsetRange(this.replaceRange.start, this.replaceRange.start + this.newText.length); + } } /** diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 85068d38984..7dbbc93a131 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -17,7 +17,6 @@ import { nullTokenizeEncoded } from '../languages/nullTokenize.js'; import { ITextModel } from '../model.js'; import { FixedArray } from './fixedArray.js'; import { IModelContentChange } from '../textModelEvents.js'; -import { ITokenizeLineWithEditResult, LineEditWithAdditionalLines } from '../tokenizationTextModelPart.js'; import { ContiguousMultilineTokensBuilder } from '../tokens/contiguousMultilineTokensBuilder.js'; import { LineTokens } from '../tokens/lineTokens.js'; @@ -102,38 +101,23 @@ export class TokenizerWithStateStoreAndTextModel } /** assumes state is up to date */ - public tokenizeLineWithEdit(lineNumber: number, edit: LineEditWithAdditionalLines): ITokenizeLineWithEditResult { - const lineStartState = this.getStartState(lineNumber); + public tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null { + const lineStartState: IState | null = this.getStartState(lineNumber); if (!lineStartState) { - return { mainLineTokens: null, additionalLines: null }; + return null; } - const curLineContent = this._textModel.getLineContent(lineNumber); - const newLineContent = edit.lineEdit.apply(curLineContent); - - const languageId = this._textModel.getLanguageIdAtPosition(lineNumber, 0); - const result = safeTokenize( - this._languageIdCodec, - languageId, - this.tokenizationSupport, - newLineContent, - true, - lineStartState - ); + const languageId = this._textModel.getLanguageId(); + const result: LineTokens[] = []; - let additionalLines: LineTokens[] | null = null; - if (edit.additionalLines) { - additionalLines = []; - let state = result.endState; - for (const line of edit.additionalLines) { - const r = safeTokenize(this._languageIdCodec, languageId, this.tokenizationSupport, line, true, state); - additionalLines.push(new LineTokens(r.tokens, line, this._languageIdCodec)); - state = r.endState; - } + let state = lineStartState; + for (const line of lines) { + const r = safeTokenize(this._languageIdCodec, languageId, this.tokenizationSupport, line, true, state); + result.push(new LineTokens(r.tokens, line, this._languageIdCodec)); + state = r.endState; } - const mainLineTokens = new LineTokens(result.tokens, newLineContent, this._languageIdCodec); - return { mainLineTokens, additionalLines }; + return result; } public hasAccurateTokensForLine(lineNumber: number): boolean { diff --git a/src/vs/editor/common/model/tokenizationTextModelPart.ts b/src/vs/editor/common/model/tokenizationTextModelPart.ts index 811c24b7256..f51bf1b98bc 100644 --- a/src/vs/editor/common/model/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/model/tokenizationTextModelPart.ts @@ -25,7 +25,7 @@ import { AbstractTokens, AttachedViewHandler, AttachedViews } from './tokens.js' import { TreeSitterTokens } from './treeSitterTokens.js'; import { ITreeSitterParserService } from '../services/treeSitterParserService.js'; import { IModelContentChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelTokensChangedEvent } from '../textModelEvents.js'; -import { BackgroundTokenizationState, ITokenizationTextModelPart, ITokenizeLineWithEditResult, LineEditWithAdditionalLines } from '../tokenizationTextModelPart.js'; +import { BackgroundTokenizationState, ITokenizationTextModelPart } from '../tokenizationTextModelPart.js'; import { ContiguousMultilineTokens } from '../tokens/contiguousMultilineTokens.js'; import { ContiguousMultilineTokensBuilder } from '../tokens/contiguousMultilineTokensBuilder.js'; import { ContiguousTokensStore } from '../tokens/contiguousTokensStore.js'; @@ -202,8 +202,8 @@ export class TokenizationTextModelPart extends TextModelPart implements ITokeniz return this._tokens.getTokenTypeIfInsertingCharacter(lineNumber, column, character); } - public tokenizeLineWithEdit(lineNumber: number, edit: LineEditWithAdditionalLines): ITokenizeLineWithEditResult { - return this._tokens.tokenizeLineWithEdit(lineNumber, edit); + public tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null { + return this._tokens.tokenizeLinesAt(lineNumber, lines); } // #endregion @@ -648,12 +648,13 @@ class GrammarTokens extends AbstractTokens { return this._tokenizer.getTokenTypeIfInsertingCharacter(position, character); } - public tokenizeLineWithEdit(lineNumber: number, edit: LineEditWithAdditionalLines): ITokenizeLineWithEditResult { + + public tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null { if (!this._tokenizer) { - return { mainLineTokens: null, additionalLines: null }; + return null; } this.forceTokenization(lineNumber); - return this._tokenizer.tokenizeLineWithEdit(lineNumber, edit); + return this._tokenizer.tokenizeLinesAt(lineNumber, lines); } public get hasTokens(): boolean { diff --git a/src/vs/editor/common/model/tokens.ts b/src/vs/editor/common/model/tokens.ts index 0e4ed56480c..493cd2d0662 100644 --- a/src/vs/editor/common/model/tokens.ts +++ b/src/vs/editor/common/model/tokens.ts @@ -13,7 +13,7 @@ import { ILanguageIdCodec } from '../languages.js'; import { IAttachedView } from '../model.js'; import { TextModel } from './textModel.js'; import { IModelContentChangedEvent, IModelTokensChangedEvent } from '../textModelEvents.js'; -import { BackgroundTokenizationState, ITokenizeLineWithEditResult, LineEditWithAdditionalLines } from '../tokenizationTextModelPart.js'; +import { BackgroundTokenizationState } from '../tokenizationTextModelPart.js'; import { LineTokens } from '../tokens/lineTokens.js'; /** @@ -131,7 +131,7 @@ export abstract class AbstractTokens extends Disposable { public abstract getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType; - public abstract tokenizeLineWithEdit(lineNumber: number, edit: LineEditWithAdditionalLines): ITokenizeLineWithEditResult; + public abstract tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null; public abstract get hasTokens(): boolean; } diff --git a/src/vs/editor/common/model/treeSitterTokens.ts b/src/vs/editor/common/model/treeSitterTokens.ts index 43ab00f0f0f..f4077388ef0 100644 --- a/src/vs/editor/common/model/treeSitterTokens.ts +++ b/src/vs/editor/common/model/treeSitterTokens.ts @@ -10,7 +10,6 @@ import { TextModel } from './textModel.js'; import { ITreeSitterParserService } from '../services/treeSitterParserService.js'; import { IModelContentChangedEvent } from '../textModelEvents.js'; import { AbstractTokens } from './tokens.js'; -import { ITokenizeLineWithEditResult, LineEditWithAdditionalLines } from '../tokenizationTextModelPart.js'; import { IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js'; export class TreeSitterTokens extends AbstractTokens { @@ -95,9 +94,9 @@ export class TreeSitterTokens extends AbstractTokens { // TODO @alexr00 implement once we have custom parsing and don't just feed in the whole text model value return StandardTokenType.Other; } - public override tokenizeLineWithEdit(lineNumber: number, edit: LineEditWithAdditionalLines): ITokenizeLineWithEditResult { + public override tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null { // TODO @alexr00 understand what this is for and implement - return { mainLineTokens: null, additionalLines: null }; + return null; } public override get hasTokens(): boolean { // TODO @alexr00 once we have a token store, implement properly diff --git a/src/vs/editor/common/tokenizationTextModelPart.ts b/src/vs/editor/common/tokenizationTextModelPart.ts index 25e7569b2f9..6238c606552 100644 --- a/src/vs/editor/common/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/tokenizationTextModelPart.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OffsetEdit } from './core/offsetEdit.js'; -import { OffsetRange } from './core/offsetRange.js'; import { Range } from './core/range.js'; import { StandardTokenType } from './encodedTokenAttributes.js'; import { LineTokens } from './tokens/lineTokens.js'; @@ -85,9 +83,10 @@ export interface ITokenizationTextModelPart { getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType; /** + * Tokens the lines as if they were inserted at [lineNumber, lineNumber). * @internal */ - tokenizeLineWithEdit(lineNumber: number, edit: LineEditWithAdditionalLines): ITokenizeLineWithEditResult; + tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null; getLanguageId(): string; getLanguageIdAtPosition(lineNumber: number, column: number): string; @@ -97,32 +96,6 @@ export interface ITokenizationTextModelPart { readonly backgroundTokenizationState: BackgroundTokenizationState; } -export class LineEditWithAdditionalLines { - public static replace(range: OffsetRange, text: string): LineEditWithAdditionalLines { - return new LineEditWithAdditionalLines( - OffsetEdit.replace(range, text), - null, - ); - } - - constructor( - /** - * The edit for the main line. - */ - readonly lineEdit: OffsetEdit, - - /** - * Full lines appended after the main line. - */ - readonly additionalLines: string[] | null, - ) { } -} - -export interface ITokenizeLineWithEditResult { - readonly mainLineTokens: LineTokens | null; - readonly additionalLines: LineTokens[] | null; -} - export const enum BackgroundTokenizationState { InProgress = 1, Completed = 2, diff --git a/src/vs/editor/common/tokens/lineTokens.ts b/src/vs/editor/common/tokens/lineTokens.ts index 7d2b07476d0..72d94d120d4 100644 --- a/src/vs/editor/common/tokens/lineTokens.ts +++ b/src/vs/editor/common/tokens/lineTokens.ts @@ -203,6 +203,10 @@ export class LineTokens implements IViewLineTokens { return new SliceLineTokens(this, startOffset, endOffset, deltaOffset); } + public sliceZeroCopy(range: OffsetRange): IViewLineTokens { + return this.sliceAndInflate(range.start, range.endExclusive, 0); + } + /** * @pure * @param insertTokens Must be sorted by offset. diff --git a/src/vs/editor/common/tokens/tokenArray.ts b/src/vs/editor/common/tokens/tokenArray.ts index 1b6891f223d..bc089d1604a 100644 --- a/src/vs/editor/common/tokens/tokenArray.ts +++ b/src/vs/editor/common/tokens/tokenArray.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { OffsetRange } from '../core/offsetRange.js'; +import { ILanguageIdCodec } from '../languages.js'; +import { LineTokens } from './lineTokens.js'; /** * This class represents a sequence of tokens. @@ -14,6 +16,14 @@ import { OffsetRange } from '../core/offsetRange.js'; * TODO: Make this class more efficient (e.g. by using a Int32Array). */ export class TokenArray { + public static fromLineTokens(lineTokens: LineTokens): TokenArray { + const tokenInfo: TokenInfo[] = []; + for (let i = 0; i < lineTokens.getCount(); i++) { + tokenInfo.push(new TokenInfo(lineTokens.getEndOffset(i) - lineTokens.getStartOffset(i), lineTokens.getMetadata(i))); + } + return TokenArray.create(tokenInfo); + } + public static create(tokenInfo: TokenInfo[]): TokenArray { return new TokenArray(tokenInfo); } @@ -22,6 +32,10 @@ export class TokenArray { private readonly _tokenInfo: TokenInfo[], ) { } + public toLineTokens(lineContent: string, decoder: ILanguageIdCodec): LineTokens { + return LineTokens.createFromTextAndMetadata(this.map((r, t) => ({ text: r.substring(lineContent), metadata: t.metadata })), decoder); + } + public forEach(cb: (range: OffsetRange, tokenInfo: TokenInfo) => void): void { let lengthSum = 0; for (const tokenInfo of this._tokenInfo) { @@ -31,6 +45,17 @@ export class TokenArray { } } + public map(cb: (range: OffsetRange, tokenInfo: TokenInfo) => T): T[] { + const result: T[] = []; + let lengthSum = 0; + for (const tokenInfo of this._tokenInfo) { + const range = new OffsetRange(lengthSum, lengthSum + tokenInfo.length); + result.push(cb(range, tokenInfo)); + lengthSum += tokenInfo.length; + } + return result; + } + public slice(range: OffsetRange): TokenArray { const result: TokenInfo[] = []; let lengthSum = 0; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts index befcccca81a..a240f67595d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts @@ -11,6 +11,7 @@ import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js import { SetMap } from '../../../../../base/common/map.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { ISingleEditOperation } from '../../../../common/core/editOperation.js'; +import { SingleOffsetEdit } from '../../../../common/core/offsetEdit.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; @@ -21,7 +22,6 @@ import { ILanguageConfigurationService } from '../../../../common/languages/lang import { ITextModel } from '../../../../common/model.js'; import { fixBracketsInLine } from '../../../../common/model/bracketPairsTextModelPart/fixBrackets.js'; import { TextModelText } from '../../../../common/model/textModelText.js'; -import { LineEditWithAdditionalLines } from '../../../../common/tokenizationTextModelPart.js'; import { SnippetParser, Text } from '../../../snippet/browser/snippetParser.js'; import { getReadonlyEmptyArray } from '../utils.js'; @@ -412,17 +412,15 @@ function getDefaultRange(position: Position, model: ITextModel): Range { } function closeBrackets(text: string, position: Position, model: ITextModel, languageConfigurationService: ILanguageConfigurationService): string { - const lineStart = model.getLineContent(position.lineNumber).substring(0, position.column - 1); - const newLine = lineStart + text; + const currentLine = model.getLineContent(position.lineNumber); + const edit = SingleOffsetEdit.replace(new OffsetRange(position.column - 1, currentLine.length), text); - const edit = LineEditWithAdditionalLines.replace(OffsetRange.ofStartAndLength(position.column - 1, newLine.length - (position.column - 1)), text); - const newTokens = model.tokenization.tokenizeLineWithEdit(position.lineNumber, edit); - const slicedTokens = newTokens?.mainLineTokens?.sliceAndInflate(position.column - 1, newLine.length, 0); - if (!slicedTokens) { + const proposedLineTokens = model.tokenization.tokenizeLinesAt(position.lineNumber, [edit.apply(currentLine)]); + const textTokens = proposedLineTokens?.[0].sliceZeroCopy(edit.getRangeAfterApply()); + if (!textTokens) { return text; } - const newText = fixBracketsInLine(slicedTokens, languageConfigurationService); - - return newText; + const fixedText = fixBracketsInLine(textTokens, languageConfigurationService); + return fixedText; } 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 8bff9b3efba..01df0592939 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts @@ -18,7 +18,6 @@ import { Range } from '../../../../../common/core/range.js'; import { StringBuilder } from '../../../../../common/core/stringBuilder.js'; import { ILanguageService } from '../../../../../common/languages/language.js'; import { IModelDeltaDecoration, ITextModel, InjectedTextCursorStops, PositionAffinity } from '../../../../../common/model.js'; -import { LineEditWithAdditionalLines } from '../../../../../common/tokenizationTextModelPart.js'; import { LineTokens } from '../../../../../common/tokens/lineTokens.js'; import { LineDecoration } from '../../../../../common/viewLayout/lineDecorations.js'; import { RenderLineInput, renderViewLine } from '../../../../../common/viewLayout/viewLineRenderer.js'; @@ -63,16 +62,14 @@ export class GhostTextView extends Disposable { const extraClassName = syntaxHighlightingEnabled ? ' syntax-highlighted' : ''; const { inlineTexts, additionalLines, hiddenRange } = computeGhostTextViewData(ghostText, textModel, 'ghost-text' + extraClassName); + const currentLine = textModel.getLineContent(ghostText.lineNumber); const edit = new OffsetEdit(inlineTexts.map(t => SingleOffsetEdit.insert(t.column - 1, t.text))); - const tokens = syntaxHighlightingEnabled ? textModel.tokenization.tokenizeLineWithEdit(ghostText.lineNumber, new LineEditWithAdditionalLines( - edit, - additionalLines.map(l => l.content) - )) : undefined; + const tokens = syntaxHighlightingEnabled ? textModel.tokenization.tokenizeLinesAt(ghostText.lineNumber, [edit.apply(currentLine), ...additionalLines.map(l => l.content)]) : undefined; const newRanges = edit.getNewTextRanges(); - const inlineTextsWithTokens = inlineTexts.map((t, idx) => ({ ...t, tokens: tokens?.mainLineTokens?.getTokensInRange(newRanges[idx]) })); + const inlineTextsWithTokens = inlineTexts.map((t, idx) => ({ ...t, tokens: tokens?.[0]?.getTokensInRange(newRanges[idx]) })); const tokenizedAdditionalLines: LineData[] = additionalLines.map((l, idx) => ({ - content: tokens?.additionalLines?.[idx] ?? LineTokens.createEmpty(l.content, this._languageService.languageIdCodec), + content: tokens?.[idx + 1] ?? LineTokens.createEmpty(l.content, this._languageService.languageIdCodec), decorations: l.decorations, })); From 41a58be380da2ab2211035600ed4859bce05a0bc Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 19 Dec 2024 12:52:16 +0100 Subject: [PATCH 391/479] Bug: The cursor must be within a commenting range to add a comment. (#236578) Fixes #236559 --- src/vs/workbench/contrib/comments/browser/commentNode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index f90a81288a3..89418b81318 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -457,7 +457,7 @@ export class CommentNode extends Disposable { this._register(this._reactionsActionBar); const hasReactionHandler = this.commentService.hasReactionHandler(this.owner); - this.comment.commentReactions!.filter(reaction => !!reaction.count).map(reaction => { + this.comment.commentReactions?.filter(reaction => !!reaction.count).map(reaction => { const action = new ReactionAction(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted && (reaction.canEdit || hasReactionHandler) ? 'active' : '', (reaction.canEdit || hasReactionHandler), async () => { try { await this.commentService.toggleReaction(this.owner, this.resource, this.commentThread, this.comment, reaction); From 2ba0803abfb1b749c432e4dc7a8315f98cc799a7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 19 Dec 2024 13:02:00 +0100 Subject: [PATCH 392/479] multi root - allow `--remove` for removal of workspace folders (fix #204770) (#236580) --- src/vs/platform/environment/common/argv.ts | 1 + src/vs/platform/environment/node/argv.ts | 1 + .../launch/electron-main/launchMainService.ts | 1 + .../electron-main/nativeHostMainService.ts | 1 + src/vs/platform/window/common/window.ts | 4 +- .../platform/windows/electron-main/windows.ts | 1 + .../electron-main/windowsMainService.ts | 39 ++++++----- src/vs/server/node/server.cli.ts | 1 + src/vs/workbench/api/node/extHostCLIServer.ts | 7 +- src/vs/workbench/electron-sandbox/window.ts | 67 +++++++++++-------- .../host/browser/browserHostService.ts | 21 ++++-- 11 files changed, 91 insertions(+), 53 deletions(-) diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index 87825ee7d2c..e0756ae8946 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -36,6 +36,7 @@ export interface NativeParsedArgs { diff?: boolean; merge?: boolean; add?: boolean; + remove?: boolean; goto?: boolean; 'new-window'?: boolean; 'reuse-window'?: boolean; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 4ef107c79bc..606aa8b277f 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -81,6 +81,7 @@ export const OPTIONS: OptionDescriptions> = { 'diff': { type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") }, 'merge': { type: 'boolean', cat: 'o', alias: 'm', args: ['path1', 'path2', 'base', 'result'], description: localize('merge', "Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.") }, 'add': { type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") }, + 'remove': { type: 'boolean', cat: 'o', args: 'folder', description: localize('remove', "Remove folder(s) from the last active window.") }, 'goto': { type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") }, 'new-window': { type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") }, 'reuse-window': { type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") }, diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 36020f52cfc..df93721b40e 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -207,6 +207,7 @@ export class LaunchMainService implements ILaunchMainService { diffMode: args.diff, mergeMode: args.merge, addMode: args.add, + removeMode: args.remove, noRecentEntry: !!args['skip-add-to-recently-opened'], gotoLineMode: args.goto }); diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index c3dbf1ee3ea..794c5aed575 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -220,6 +220,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain diffMode: options.diffMode, mergeMode: options.mergeMode, addMode: options.addMode, + removeMode: options.removeMode, gotoLineMode: options.gotoLineMode, noRecentEntry: options.noRecentEntry, waitMarkerFileURI: options.waitMarkerFileURI, diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index d547c37bf50..3a03481e55a 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -63,6 +63,7 @@ export interface IOpenWindowOptions extends IBaseOpenWindowsOptions { readonly noRecentEntry?: boolean; readonly addMode?: boolean; + readonly removeMode?: boolean; readonly diffMode?: boolean; readonly mergeMode?: boolean; @@ -71,8 +72,9 @@ export interface IOpenWindowOptions extends IBaseOpenWindowsOptions { readonly waitMarkerFileURI?: URI; } -export interface IAddFoldersRequest { +export interface IAddRemoveFoldersRequest { readonly foldersToAdd: UriComponents[]; + readonly foldersToRemove: UriComponents[]; } interface IOpenedWindow { diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index dd5148103bd..6d14080654d 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -103,6 +103,7 @@ export interface IOpenConfiguration extends IBaseOpenConfiguration { readonly diffMode?: boolean; readonly mergeMode?: boolean; addMode?: boolean; + removeMode?: boolean; readonly gotoLineMode?: boolean; readonly initialStartup?: boolean; readonly noRecentEntry?: boolean; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index d403852efcc..8db5f04b7e3 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -37,7 +37,7 @@ import product from '../../product/common/product.js'; import { IProtocolMainService } from '../../protocol/electron-main/protocol.js'; import { getRemoteAuthority } from '../../remote/common/remoteHosts.js'; import { IStateService } from '../../state/node/state.js'; -import { IAddFoldersRequest, INativeOpenFileRequest, INativeWindowConfiguration, IOpenEmptyWindowOptions, IPath, IPathsToWaitFor, isFileToOpen, isFolderToOpen, isWorkspaceToOpen, IWindowOpenable, IWindowSettings } from '../../window/common/window.js'; +import { IAddRemoveFoldersRequest, INativeOpenFileRequest, INativeWindowConfiguration, IOpenEmptyWindowOptions, IPath, IPathsToWaitFor, isFileToOpen, isFolderToOpen, isWorkspaceToOpen, IWindowOpenable, IWindowSettings } from '../../window/common/window.js'; import { CodeWindow } from './windowImpl.js'; import { IOpenConfiguration, IOpenEmptyConfiguration, IWindowsCountChangedEvent, IWindowsMainService, OpenContext, getLastFocused } from './windows.js'; import { findWindowOnExtensionDevelopmentPath, findWindowOnFile, findWindowOnWorkspaceOrFolder } from './windowsFinder.js'; @@ -287,11 +287,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic async open(openConfig: IOpenConfiguration): Promise { this.logService.trace('windowsManager#open'); - if (openConfig.addMode && (openConfig.initialStartup || !this.getLastActiveWindow())) { - openConfig.addMode = false; // Make sure addMode is only enabled if we have an active window + // Make sure addMode/removeMode is only enabled if we have an active window + if ((openConfig.addMode || openConfig.removeMode) && (openConfig.initialStartup || !this.getLastActiveWindow())) { + openConfig.addMode = false; + openConfig.removeMode = false; } const foldersToAdd: ISingleFolderWorkspacePathToOpen[] = []; + const foldersToRemove: ISingleFolderWorkspacePathToOpen[] = []; + const foldersToOpen: ISingleFolderWorkspacePathToOpen[] = []; const workspacesToOpen: IWorkspacePathToOpen[] = []; @@ -311,6 +315,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // When run with --add, take the folders that are to be opened as // folders that should be added to the currently active window. foldersToAdd.push(path); + } else if (openConfig.removeMode) { + // When run with --remove, take the folders that are to be opened as + // folders that should be removed from the currently active window. + foldersToRemove.push(path); } else { foldersToOpen.push(path); } @@ -360,7 +368,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Open based on config - const { windows: usedWindows, filesOpenedInWindow } = await this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyWindowsWithBackupsToRestore, openOneEmptyWindow, filesToOpen, foldersToAdd); + const { windows: usedWindows, filesOpenedInWindow } = await this.doOpen(openConfig, workspacesToOpen, foldersToOpen, emptyWindowsWithBackupsToRestore, openOneEmptyWindow, filesToOpen, foldersToAdd, foldersToRemove); this.logService.trace(`windowsManager#open used window count ${usedWindows.length} (workspacesToOpen: ${workspacesToOpen.length}, foldersToOpen: ${foldersToOpen.length}, emptyToRestore: ${emptyWindowsWithBackupsToRestore.length}, openOneEmptyWindow: ${openOneEmptyWindow})`); @@ -463,7 +471,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic emptyToRestore: IEmptyWindowBackupInfo[], openOneEmptyWindow: boolean, filesToOpen: IFilesToOpen | undefined, - foldersToAdd: ISingleFolderWorkspacePathToOpen[] + foldersToAdd: ISingleFolderWorkspacePathToOpen[], + foldersToRemove: ISingleFolderWorkspacePathToOpen[] ): Promise<{ windows: ICodeWindow[]; filesOpenedInWindow: ICodeWindow | undefined }> { // Keep track of used windows and remember @@ -482,12 +491,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Settings can decide if files/folders open in new window or not let { openFolderInNewWindow, openFilesInNewWindow } = this.shouldOpenNewWindow(openConfig); - // Handle folders to add by looking for the last active workspace (not on initial startup) - if (!openConfig.initialStartup && foldersToAdd.length > 0) { - const authority = foldersToAdd[0].remoteAuthority; + // Handle folders to add/remove by looking for the last active workspace (not on initial startup) + if (!openConfig.initialStartup && (foldersToAdd.length > 0 || foldersToRemove.length > 0)) { + const authority = foldersToAdd.at(0)?.remoteAuthority ?? foldersToRemove.at(0)?.remoteAuthority; const lastActiveWindow = this.getLastActiveWindowForAuthority(authority); if (lastActiveWindow) { - addUsedWindow(this.doAddFoldersToExistingWindow(lastActiveWindow, foldersToAdd.map(folderToAdd => folderToAdd.workspace.uri))); + addUsedWindow(this.doAddRemoveFoldersInExistingWindow(lastActiveWindow, foldersToAdd.map(folderToAdd => folderToAdd.workspace.uri), foldersToRemove.map(folderToRemove => folderToRemove.workspace.uri))); } } @@ -671,13 +680,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic windowToFocus.focus(); } - private doAddFoldersToExistingWindow(window: ICodeWindow, foldersToAdd: URI[]): ICodeWindow { - this.logService.trace('windowsManager#doAddFoldersToExistingWindow', { foldersToAdd }); + private doAddRemoveFoldersInExistingWindow(window: ICodeWindow, foldersToAdd: URI[], foldersToRemove: URI[]): ICodeWindow { + this.logService.trace('windowsManager#doAddRemoveFoldersToExistingWindow', { foldersToAdd, foldersToRemove }); window.focus(); // make sure window has focus - const request: IAddFoldersRequest = { foldersToAdd }; - window.sendWhenReady('vscode:addFolders', CancellationToken.None, request); + const request: IAddRemoveFoldersRequest = { foldersToAdd, foldersToRemove }; + window.sendWhenReady('vscode:addRemoveFolders', CancellationToken.None, request); return window; } @@ -764,10 +773,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } // Handle the case of multiple folders being opened from CLI while we are - // not in `--add` mode by creating an untitled workspace, only if: + // not in `--add` or `--remove` mode by creating an untitled workspace, only if: // - they all share the same remote authority // - there is no existing workspace to open that matches these folders - if (!openConfig.addMode && isCommandLineOrAPICall) { + if (!openConfig.addMode && !openConfig.removeMode && isCommandLineOrAPICall) { const foldersToOpen = pathsToOpen.filter(path => isSingleFolderWorkspacePathToOpen(path)); if (foldersToOpen.length > 1) { const remoteAuthority = foldersToOpen[0].remoteAuthority; diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index 7f20588c3bd..0535ddd998f 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -337,6 +337,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise { - const { fileURIs, folderURIs, forceNewWindow, diffMode, mergeMode, addMode, forceReuseWindow, gotoLineMode, waitMarkerFilePath, remoteAuthority } = data; + const { fileURIs, folderURIs, forceNewWindow, diffMode, mergeMode, addMode, removeMode, forceReuseWindow, gotoLineMode, waitMarkerFilePath, remoteAuthority } = data; const urisToOpen: IWindowOpenable[] = []; if (Array.isArray(folderURIs)) { for (const s of folderURIs) { @@ -144,8 +145,8 @@ export class CLIServerBase { } } const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined; - const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode; - const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, mergeMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI, remoteAuthority }; + const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode && !removeMode; + const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, mergeMode, addMode, removeMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI, remoteAuthority }; this._commands.executeCommand('_remoteCLI.windowOpen', urisToOpen, windowOpenArgs); } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index c186b45d2ba..4babd90fe70 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -14,7 +14,7 @@ import { IFileService } from '../../platform/files/common/files.js'; import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors, IResourceDiffEditorInput, IUntypedEditorInput, IEditorPane, isResourceEditorInput, IResourceMergeEditorInput } from '../common/editor.js'; import { IEditorService } from '../services/editor/common/editorService.js'; import { ITelemetryService } from '../../platform/telemetry/common/telemetry.js'; -import { WindowMinimumSize, IOpenFileRequest, IAddFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest, hasNativeTitlebar } from '../../platform/window/common/window.js'; +import { WindowMinimumSize, IOpenFileRequest, IAddRemoveFoldersRequest, INativeRunActionInWindowRequest, INativeRunKeybindingInWindowRequest, INativeOpenFileRequest, hasNativeTitlebar } from '../../platform/window/common/window.js'; import { ITitleService } from '../services/title/browser/titleService.js'; import { IWorkbenchThemeService } from '../services/themes/common/workbenchThemeService.js'; import { ApplyZoomTarget, applyZoom } from '../../platform/window/electron-sandbox/window.js'; @@ -84,8 +84,9 @@ export class NativeWindow extends BaseWindow { private readonly customTitleContextMenuDisposable = this._register(new DisposableStore()); - private readonly addFoldersScheduler = this._register(new RunOnceScheduler(() => this.doAddFolders(), 100)); + private readonly addRemoveFoldersScheduler = this._register(new RunOnceScheduler(() => this.doAddRemoveFolders(), 100)); private pendingFoldersToAdd: URI[] = []; + private pendingFoldersToRemove: URI[] = []; private isDocumentedEdited = false; @@ -209,11 +210,11 @@ export class NativeWindow extends BaseWindow { // Support openFiles event for existing and new files ipcRenderer.on('vscode:openFiles', (event: unknown, request: IOpenFileRequest) => { this.onOpenFiles(request); }); - // Support addFolders event if we have a workspace opened - ipcRenderer.on('vscode:addFolders', (event: unknown, request: IAddFoldersRequest) => { this.onAddFoldersRequest(request); }); + // Support addRemoveFolders event for workspace management + ipcRenderer.on('vscode:addRemoveFolders', (event: unknown, request: IAddRemoveFoldersRequest) => this.onAddRemoveFoldersRequest(request)); // Message support - ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => { this.notificationService.info(message); }); + ipcRenderer.on('vscode:showInfoMessage', (event: unknown, message: string) => this.notificationService.info(message)); // Shell Environment Issue Notifications ipcRenderer.on('vscode:showResolveShellEnvError', (event: unknown, message: string) => { @@ -788,20 +789,6 @@ export class NativeWindow extends BaseWindow { }); } - private async openTunnel(address: string, port: number): Promise { - const remoteAuthority = this.environmentService.remoteAuthority; - const addressProvider: IAddressProvider | undefined = remoteAuthority ? { - getAddress: async (): Promise => { - return (await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority)).authority; - } - } : undefined; - const tunnel = await this.tunnelService.getExistingTunnel(address, port); - if (!tunnel || (typeof tunnel === 'string')) { - return this.tunnelService.openTunnel(addressProvider, address, port); - } - return tunnel; - } - async resolveExternalUri(uri: URI, options?: OpenOptions): Promise { let queryTunnel: RemoteTunnel | string | undefined; if (options?.allowTunneling) { @@ -826,6 +813,7 @@ export class NativeWindow extends BaseWindow { } } } + if (portMappingRequest) { const tunnel = await this.openTunnel(portMappingRequest.address, portMappingRequest.port); if (tunnel && (typeof tunnel !== 'string')) { @@ -861,6 +849,22 @@ export class NativeWindow extends BaseWindow { return undefined; } + private async openTunnel(address: string, port: number): Promise { + const remoteAuthority = this.environmentService.remoteAuthority; + const addressProvider: IAddressProvider | undefined = remoteAuthority ? { + getAddress: async (): Promise => { + return (await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority)).authority; + } + } : undefined; + + const tunnel = await this.tunnelService.getExistingTunnel(address, port); + if (!tunnel || (typeof tunnel === 'string')) { + return this.tunnelService.openTunnel(addressProvider, address, port); + } + + return tunnel; + } + private setupOpenHandlers(): void { // Handle external open() calls @@ -961,27 +965,32 @@ export class NativeWindow extends BaseWindow { //#endregion - private onAddFoldersRequest(request: IAddFoldersRequest): void { + private onAddRemoveFoldersRequest(request: IAddRemoveFoldersRequest): void { // Buffer all pending requests this.pendingFoldersToAdd.push(...request.foldersToAdd.map(folder => URI.revive(folder))); + this.pendingFoldersToRemove.push(...request.foldersToRemove.map(folder => URI.revive(folder))); // Delay the adding of folders a bit to buffer in case more requests are coming - if (!this.addFoldersScheduler.isScheduled()) { - this.addFoldersScheduler.schedule(); + if (!this.addRemoveFoldersScheduler.isScheduled()) { + this.addRemoveFoldersScheduler.schedule(); } } - private doAddFolders(): void { - const foldersToAdd: IWorkspaceFolderCreationData[] = []; - - for (const folder of this.pendingFoldersToAdd) { - foldersToAdd.push(({ uri: folder })); - } + private async doAddRemoveFolders(): Promise { + const foldersToAdd: IWorkspaceFolderCreationData[] = this.pendingFoldersToAdd.map(folder => ({ uri: folder })); + const foldersToRemove = this.pendingFoldersToRemove.slice(0); this.pendingFoldersToAdd = []; + this.pendingFoldersToRemove = []; + + if (foldersToAdd.length) { + await this.workspaceEditingService.addFolders(foldersToAdd); + } - this.workspaceEditingService.addFolders(foldersToAdd); + if (foldersToRemove.length) { + await this.workspaceEditingService.removeFolders(foldersToRemove); + } } private async onOpenFiles(request: INativeOpenFileRequest): Promise { diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index afd2a3d1267..57cb6e482cf 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -40,6 +40,7 @@ import { coalesce } from '../../../../base/common/arrays.js'; import { mainWindow, isAuxiliaryWindow } from '../../../../base/browser/window.js'; import { isIOS, isMacintosh } from '../../../../base/common/platform.js'; import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; +import { URI } from '../../../../base/common/uri.js'; enum HostShutdownReason { @@ -238,7 +239,9 @@ export class BrowserHostService extends Disposable implements IHostService { private async doOpenWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise { const payload = this.preservePayload(false /* not an empty window */, options); const fileOpenables: IFileToOpen[] = []; + const foldersToAdd: IWorkspaceFolderCreationData[] = []; + const foldersToRemove: URI[] = []; for (const openable of toOpen) { openable.label = openable.label || this.getRecentLabel(openable); @@ -246,7 +249,9 @@ export class BrowserHostService extends Disposable implements IHostService { // Folder if (isFolderToOpen(openable)) { if (options?.addMode) { - foldersToAdd.push(({ uri: openable.folderUri })); + foldersToAdd.push({ uri: openable.folderUri }); + } else if (options?.removeMode) { + foldersToRemove.push(openable.folderUri); } else { this.doOpen({ folderUri: openable.folderUri }, { reuse: this.shouldReuse(options, false /* no file */), payload }); } @@ -263,11 +268,17 @@ export class BrowserHostService extends Disposable implements IHostService { } } - // Handle Folders to Add - if (foldersToAdd.length > 0) { - this.withServices(accessor => { + // Handle Folders to add or remove + if (foldersToAdd.length > 0 || foldersToRemove.length > 0) { + this.withServices(async accessor => { const workspaceEditingService: IWorkspaceEditingService = accessor.get(IWorkspaceEditingService); - workspaceEditingService.addFolders(foldersToAdd); + if (foldersToAdd.length > 0) { + await workspaceEditingService.addFolders(foldersToAdd); + } + + if (foldersToRemove.length > 0) { + await workspaceEditingService.removeFolders(foldersToRemove); + } }); } From 225d1ca870a984369bde1a7fcd75f863fc69fee1 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 19 Dec 2024 13:07:00 +0100 Subject: [PATCH 393/479] Improved value formatting in observable logging (#236579) * Improved value formatting in observable logging * substr -> substring --- src/vs/base/common/observableInternal/logging.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/base/common/observableInternal/logging.ts b/src/vs/base/common/observableInternal/logging.ts index 0c343b548e4..a6a5bb78a06 100644 --- a/src/vs/base/common/observableInternal/logging.ts +++ b/src/vs/base/common/observableInternal/logging.ts @@ -356,6 +356,14 @@ function formatArray(value: unknown[], availableLen: number): string { } function formatObject(value: object, availableLen: number): string { + if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) { + const val = value.toString(); + if (val.length <= availableLen) { + return val; + } + return val.substring(0, availableLen - 3) + '...'; + } + let result = '{ '; let first = true; for (const [key, val] of Object.entries(value)) { From 9813e9a4a95b2c1c74fddf911ab09855ee6ba59c Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 19 Dec 2024 13:59:41 +0100 Subject: [PATCH 394/479] Revert "Pressing alt/opt makes the hover temporarily sticky (#236356)" This reverts commit 317d55da7b91e07131403663d8aa6f9499e3a112. --- .../hover/browser/contentHoverController.ts | 39 +++---------------- .../browser/contentHoverWidgetWrapper.ts | 26 +++---------- 2 files changed, 12 insertions(+), 53 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index 09cb6efcab4..11ddb36fa83 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, SHOW_OR_FOCUS_HOVER_ACTION_ID } from './hoverActionIds.js'; -import { IKeyboardEvent, StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; +import { IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent } from '../../../browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../common/config/editorOptions.js'; import { Range } from '../../../common/core/range.js'; @@ -22,9 +22,6 @@ import { ContentHoverWidgetWrapper } from './contentHoverWidgetWrapper.js'; import './hover.css'; import { Emitter } from '../../../../base/common/event.js'; import { isOnColorDecorator } from '../../colorPicker/browser/hoverColorPicker/hoverColorPicker.js'; -import { KeyCode } from '../../../../base/common/keyCodes.js'; -import { EventType } from '../../../../base/browser/dom.js'; -import { mainWindow } from '../../../../base/browser/window.js'; // sticky hover widget which doesn't disappear on focus out and such const _sticky = false @@ -95,18 +92,11 @@ export class ContentHoverController extends Disposable implements IEditorContrib this._listenersStore.add(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); this._listenersStore.add(this._editor.onMouseUp(() => this._onEditorMouseUp())); this._listenersStore.add(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(e))); + this._listenersStore.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); this._listenersStore.add(this._editor.onMouseLeave((e) => this._onEditorMouseLeave(e))); this._listenersStore.add(this._editor.onDidChangeModel(() => this._cancelSchedulerAndHide())); this._listenersStore.add(this._editor.onDidChangeModelContent(() => this._cancelScheduler())); this._listenersStore.add(this._editor.onDidScrollChange((e: IScrollEvent) => this._onEditorScrollChanged(e))); - const keyDownListener = (e: KeyboardEvent) => this._onKeyDown(e); - const keyUpListener = (e: KeyboardEvent) => this._onKeyUp(e); - mainWindow.addEventListener(EventType.KEY_DOWN, keyDownListener); - mainWindow.addEventListener(EventType.KEY_UP, keyUpListener); - this._listenersStore.add(toDisposable(() => { - mainWindow.removeEventListener(EventType.KEY_DOWN, keyDownListener); - mainWindow.removeEventListener(EventType.KEY_UP, keyUpListener); - })); } private _unhookListeners(): void { @@ -165,9 +155,6 @@ export class ContentHoverController extends Disposable implements IEditorContrib if (_sticky) { return; } - if (this._contentWidget) { - this._contentWidget.temporarilySticky = false; - } this.hideContentHover(); } @@ -247,15 +234,11 @@ export class ContentHoverController extends Disposable implements IEditorContrib this.hideContentHover(); } - private _onKeyDown(e: KeyboardEvent): void { - if (!this._contentWidget) { + private _onKeyDown(e: IKeyboardEvent): void { + if (!this._editor.hasModel()) { return; } - const event = new StandardKeyboardEvent(e); - if (event.keyCode === KeyCode.Alt) { - this._contentWidget.temporarilySticky = true; - } - const isPotentialKeyboardShortcut = this._isPotentialKeyboardShortcut(event); + const isPotentialKeyboardShortcut = this._isPotentialKeyboardShortcut(e); if (isPotentialKeyboardShortcut) { return; } @@ -265,16 +248,6 @@ export class ContentHoverController extends Disposable implements IEditorContrib this.hideContentHover(); } - private _onKeyUp(e: KeyboardEvent): void { - if (!this._contentWidget) { - return; - } - const event = new StandardKeyboardEvent(e); - if (event.keyCode === KeyCode.Alt) { - this._contentWidget.temporarilySticky = false; - } - } - private _isPotentialKeyboardShortcut(e: IKeyboardEvent): boolean { if (!this._editor.hasModel() || !this._contentWidget) { return false; diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts index 228e9533d29..ad9a9920967 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts @@ -27,7 +27,6 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge private _currentResult: ContentHoverResult | null = null; private _renderedContentHover: RenderedContentHover | undefined; - private _temporarilySticky: boolean = false; private readonly _contentHoverWidget: ContentHoverWidget; private readonly _participants: IEditorHoverParticipant[]; @@ -163,25 +162,11 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge if (currentHoverResultIsEmpty) { currentHoverResult = null; } - const hoverVisible = this._contentHoverWidget.isVisible; - if (!hoverVisible) { - this._renderResult(currentHoverResult); - } else { - if (this._temporarilySticky) { - return; - } else { - this._renderResult(currentHoverResult); - } - } - } - - private _renderResult(currentHoverResult: ContentHoverResult | null): void { this._currentResult = currentHoverResult; if (this._currentResult) { this._showHover(this._currentResult); } else { - this._contentHoverWidget.hide(); - this._participants.forEach(participant => participant.handleHide?.()); + this._hideHover(); } } @@ -230,6 +215,11 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge } } + private _hideHover(): void { + this._contentHoverWidget.hide(); + this._participants.forEach(participant => participant.handleHide?.()); + } + private _getHoverContext(): IEditorHoverContext { const hide = () => { this.hide(); @@ -303,10 +293,6 @@ export class ContentHoverWidgetWrapper extends Disposable implements IHoverWidge } } - public set temporarilySticky(value: boolean) { - this._temporarilySticky = value; - } - public startShowingAtRange(range: Range, mode: HoverStartMode, source: HoverStartSource, focus: boolean): void { this._startShowingOrUpdateHover(new HoverRangeAnchor(0, range, undefined, undefined), mode, source, focus, null); } From aea3ab47f5d820198b0f68d34b3846b483f9e809 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 19 Dec 2024 14:07:44 +0100 Subject: [PATCH 395/479] Fix compilation errors --- .../editor/contrib/hover/browser/contentHoverController.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index 11ddb36fa83..acbabe6a83e 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -22,6 +22,7 @@ import { ContentHoverWidgetWrapper } from './contentHoverWidgetWrapper.js'; import './hover.css'; import { Emitter } from '../../../../base/common/event.js'; import { isOnColorDecorator } from '../../colorPicker/browser/hoverColorPicker/hoverColorPicker.js'; +import { KeyCode } from '../../../../base/common/keyCodes.js'; // sticky hover widget which doesn't disappear on focus out and such const _sticky = false @@ -235,14 +236,14 @@ export class ContentHoverController extends Disposable implements IEditorContrib } private _onKeyDown(e: IKeyboardEvent): void { - if (!this._editor.hasModel()) { + if (!this._contentWidget) { return; } const isPotentialKeyboardShortcut = this._isPotentialKeyboardShortcut(e); if (isPotentialKeyboardShortcut) { return; } - if (this._contentWidget.isFocused && event.keyCode === KeyCode.Tab) { + if (this._contentWidget.isFocused && e.keyCode === KeyCode.Tab) { return; } this.hideContentHover(); From 5385e31b3f7883a5524deec90f1df5faa49c4287 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 19 Dec 2024 14:10:37 +0100 Subject: [PATCH 396/479] Trigger inline edits on paste (#236584) --- src/vs/editor/browser/observableCodeEditor.ts | 13 ++++++++++++- .../controller/inlineCompletionsController.ts | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index d8607ae319a..3633d4cac45 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -14,7 +14,7 @@ import { Selection } from '../common/core/selection.js'; import { ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; import { IModelDeltaDecoration, ITextModel } from '../common/model.js'; import { IModelContentChangedEvent } from '../common/textModelEvents.js'; -import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IOverlayWidget, IOverlayWidgetPosition } from './editorBrowser.js'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IOverlayWidget, IOverlayWidgetPosition, IPasteEvent } from './editorBrowser.js'; import { Point } from './point.js'; /** @@ -94,6 +94,16 @@ export class ObservableCodeEditor extends Disposable { } })); + this._register(this.editor.onDidPaste((e) => { + this._beginUpdate(); + try { + this._forceUpdate(); + this.onDidPaste.trigger(this._currentTransaction, e); + } finally { + this._endUpdate(); + } + })); + this._register(this.editor.onDidChangeModelContent(e => { this._beginUpdate(); try { @@ -213,6 +223,7 @@ export class ObservableCodeEditor extends Disposable { public readonly cursorLineNumber = derived(this, reader => this.cursorPosition.read(reader)?.lineNumber ?? null); public readonly onDidType = observableSignal(this); + public readonly onDidPaste = observableSignal(this); public readonly scrollTop = observableFromEvent(this.editor.onDidScrollChange, () => this.editor.getScrollTop()); public readonly scrollLeft = observableFromEvent(this.editor.onDidScrollChange, () => this.editor.getScrollLeft()); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 7c7e4495719..79fdfb85afd 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -140,6 +140,12 @@ export class InlineCompletionsController extends Disposable { } })); + this._register(runOnChange(this._editorObs.onDidPaste, (_value, _changes) => { + if (this._enabled.get()) { + this.model.get()?.trigger(); + } + })); + this._register(this._commandService.onDidExecuteCommand((e) => { // These commands don't trigger onDidType. const commands = new Set([ From 05519998aeb60a492478948e6e196586a24c04c6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 19 Dec 2024 05:10:51 -0800 Subject: [PATCH 397/479] Ignore bg terminals for confirmOnExit Fixes #235575 --- .../contrib/terminal/browser/terminalService.ts | 10 +++++++--- .../contrib/terminal/common/terminalConfiguration.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 70d2aa007ec..698559d3b2c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -95,6 +95,10 @@ export class TerminalService extends Disposable implements ITerminalService { get instances(): ITerminalInstance[] { return this._terminalGroupService.instances.concat(this._terminalEditorService.instances).concat(this._backgroundedTerminalInstances); } + /** Gets all non-background terminals. */ + get foregroundInstances(): ITerminalInstance[] { + return this._terminalGroupService.instances.concat(this._terminalEditorService.instances); + } get detachedInstances(): Iterable { return this._detachedXterms; } @@ -417,7 +421,6 @@ export class TerminalService extends Disposable implements ITerminalService { if (instance.target !== TerminalLocation.Editor && instance.hasChildProcesses && (this._terminalConfigurationService.config.confirmOnKill === 'panel' || this._terminalConfigurationService.config.confirmOnKill === 'always')) { - const veto = await this._showTerminalCloseConfirmation(true); if (veto) { return; @@ -904,10 +907,11 @@ export class TerminalService extends Disposable implements ITerminalService { protected async _showTerminalCloseConfirmation(singleTerminal?: boolean): Promise { let message: string; - if (this.instances.length === 1 || singleTerminal) { + const foregroundInstances = this.foregroundInstances; + if (foregroundInstances.length === 1 || singleTerminal) { message = nls.localize('terminalService.terminalCloseConfirmationSingular', "Do you want to terminate the active terminal session?"); } else { - message = nls.localize('terminalService.terminalCloseConfirmationPlural', "Do you want to terminate the {0} active terminal sessions?", this.instances.length); + message = nls.localize('terminalService.terminalCloseConfirmationPlural', "Do you want to terminate the {0} active terminal sessions?", foregroundInstances.length); } const { confirmed } = await this._dialogService.confirm({ type: 'warning', diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index beea86681fb..b90a67cbbc6 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -366,7 +366,7 @@ const terminalConfiguration: IConfigurationNode = { default: 'never' }, [TerminalSettingId.ConfirmOnKill]: { - description: localize('terminal.integrated.confirmOnKill', "Controls whether to confirm killing terminals when they have child processes. When set to editor, terminals in the editor area will be marked as changed when they have child processes. Note that child process detection may not work well for shells like Git Bash which don't run their processes as child processes of the shell."), + description: localize('terminal.integrated.confirmOnKill', "Controls whether to confirm killing terminals when they have child processes. When set to editor, terminals in the editor area will be marked as changed when they have child processes. Note that child process detection may not work well for shells like Git Bash which don't run their processes as child processes of the shell. Background terminals like those launched by some extensions will not trigger the confirmation."), type: 'string', enum: ['never', 'editor', 'panel', 'always'], enumDescriptions: [ From 4051adf0a86f9ec6543be6b27840614c816a7323 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:10:05 +0100 Subject: [PATCH 398/479] Fix TypeError for undefined isShowingFilterResults (#236590) fix #236521 --- src/vs/workbench/contrib/files/browser/views/explorerView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index e77296ffed9..1c718837501 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -964,7 +964,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { } hasPhantomElements(): boolean { - return this.findProvider.isShowingFilterResults(); + return !!this.findProvider?.isShowingFilterResults(); } override dispose(): void { From 25b88b7e4a8f1a96487896ca8b5fe8aa045af54e Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:46:46 +0100 Subject: [PATCH 399/479] Git - fix encoding issue with stage selected ranges (#236484) --- extensions/git/src/git.ts | 4 ++-- extensions/git/src/repository.ts | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index f2b9100d79a..d6a6bd52cfc 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1657,9 +1657,9 @@ export class Repository { await this.exec(args); } - async stage(path: string, data: string): Promise { + async stage(path: string, data: string, encoding: string): Promise { const child = this.stream(['hash-object', '--stdin', '-w', '--path', sanitizePath(path)], { stdio: [null, null, null] }); - child.stdin!.end(data, 'utf8'); + child.stdin!.end(iconv.encode(data, encoding)); const { exitCode, stdout } = await exec(child); const hash = stdout.toString('utf8'); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ac2649242f2..0c4b50cd5e5 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -7,6 +7,7 @@ import TelemetryReporter from '@vscode/extension-telemetry'; import * as fs from 'fs'; import * as path from 'path'; import picomatch from 'picomatch'; +import * as iconv from '@vscode/iconv-lite-umd'; import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, FileDecoration, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, QuickDiffProvider, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, Uri, window, workspace, WorkspaceEdit } from 'vscode'; import { ActionButton } from './actionButton'; import { ApiRepository } from './api/api1'; @@ -24,6 +25,7 @@ import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; +import { detectEncoding } from './encoding'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -1215,7 +1217,17 @@ export class Repository implements Disposable { async stage(resource: Uri, contents: string): Promise { const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/'); await this.run(Operation.Stage, async () => { - await this.repository.stage(path, contents); + const configFiles = workspace.getConfiguration('files', Uri.file(resource.fsPath)); + let encoding = configFiles.get('encoding') ?? 'utf8'; + const autoGuessEncoding = configFiles.get('autoGuessEncoding') === true; + const candidateGuessEncodings = configFiles.get('candidateGuessEncodings') ?? []; + + if (autoGuessEncoding) { + encoding = detectEncoding(Buffer.from(contents), candidateGuessEncodings) ?? encoding; + } + + encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; + await this.repository.stage(path, contents, encoding); this._onDidChangeOriginalResource.fire(resource); this.closeDiffEditors([], [...resource.fsPath]); From 986871f71ae46e0b7b27362f1456986abc7a1fbe Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 19 Dec 2024 16:20:19 +0100 Subject: [PATCH 400/479] Proper fix for #236537 (#236596) - Support installEveryWhere option by installing the extension only on servers on which it is not installed --- .../browser/extensionsWorkbenchService.ts | 47 ++++++++++++++--- .../contrib/extensions/common/extensions.ts | 1 + .../common/extensionManagement.ts | 3 +- .../common/extensionManagementService.ts | 50 +++++++++++++++---- .../test/browser/workbenchTestServices.ts | 3 +- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 0967e7d34c7..33692adf148 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -2342,35 +2342,69 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } else { let installableInfo: IExtensionInfo | undefined; let gallery: IGalleryExtension | undefined; + + // Install by id if (isString(arg)) { extension = this.local.find(e => areSameExtensions(e.identifier, { id: arg })); if (!extension?.isBuiltin) { installableInfo = { id: arg, version: installOptions.version, preRelease: installOptions.installPreReleaseVersion ?? this.preferPreReleases }; } - } else if (arg.gallery) { + } + // Install by gallery + else if (arg.gallery) { extension = arg; gallery = arg.gallery; if (installOptions.version && installOptions.version !== gallery?.version) { installableInfo = { id: extension.identifier.id, version: installOptions.version }; } - } else if (arg.resourceExtension) { + } + // Install by resource + else if (arg.resourceExtension) { extension = arg; installable = arg.resourceExtension; } + if (installableInfo) { const targetPlatform = extension?.server ? await extension.server.extensionManagementService.getTargetPlatform() : undefined; gallery = (await this.galleryService.getExtensions([installableInfo], { targetPlatform }, CancellationToken.None)).at(0); } + if (!extension && gallery) { extension = this.instantiationService.createInstance(Extension, ext => this.getExtensionState(ext), ext => this.getRuntimeState(ext), undefined, undefined, gallery, undefined); (extension).setExtensionsControlManifest(await this.extensionManagementService.getExtensionsControlManifest()); } + if (extension?.isMalicious) { throw new Error(nls.localize('malicious', "This extension is reported to be problematic.")); } - // TODO: @sandy081 - Install the extension only on servers where it is not installed - // Do not install if requested to enable and extension is already installed - if (installOptions.installEverywhere || !(installOptions.enable && extension?.local)) { + + if (gallery) { + // If requested to install everywhere + // then install the extension in all the servers where it is not installed + if (installOptions.installEverywhere) { + installOptions.servers = []; + const installableServers = await this.extensionManagementService.getInstallableServers(gallery); + for (const extensionsServer of this.extensionsServers) { + if (installableServers.includes(extensionsServer.server) && !extensionsServer.local.find(e => areSameExtensions(e.identifier, gallery.identifier))) { + installOptions.servers.push(extensionsServer.server); + } + } + } + // If requested to enable and extension is already installed + // Check if the extension is disabled because of extension kind + // If so, install the extension in the server that is compatible. + else if (installOptions.enable && extension?.local) { + installOptions.servers = []; + if (extension.enablementState === EnablementState.DisabledByExtensionKind) { + const [installableServer] = await this.extensionManagementService.getInstallableServers(gallery); + if (installableServer) { + installOptions.servers.push(installableServer); + } + } + } + } + + if (!installOptions.servers || installOptions.servers.length) { if (!installable) { if (!gallery) { const id = isString(arg) ? arg : (arg).identifier.id; @@ -2734,8 +2768,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private installFromGallery(extension: IExtension, gallery: IGalleryExtension, installOptions?: InstallExtensionOptions): Promise { installOptions = installOptions ?? {}; installOptions.pinned = extension.local?.pinned || !this.shouldAutoUpdateExtension(extension); - // TODO: @sandy081 - Install the extension only on servers where it is not installed - if (!installOptions.installEverywhere && extension.local) { + if (extension.local && !installOptions.servers) { installOptions.productVersion = this.getProductVersion(); installOptions.operation = InstallOperation.Update; return this.extensionManagementService.updateFromGallery(gallery, extension.local, installOptions); diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 4cd9d5ea1d4..e8f26f634ca 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -112,6 +112,7 @@ export interface InstallExtensionOptions extends IWorkbenchInstallOptions { version?: string; justification?: string | { reason: string; action: string }; enable?: boolean; + installEverywhere?: boolean; } export interface IExtensionsNotification { diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 2bc9bc6e793..1deee908d89 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -60,7 +60,7 @@ export type DidUninstallExtensionOnServerEvent = DidUninstallExtensionEvent & { export type DidChangeProfileForServerEvent = DidChangeProfileEvent & { server: IExtensionManagementServer }; export interface IWorkbenchInstallOptions extends InstallOptions { - readonly installEverywhere?: boolean; + servers?: IExtensionManagementServer[]; } export const IWorkbenchExtensionManagementService = refineServiceDecorator(IProfileAwareExtensionManagementService); @@ -84,6 +84,7 @@ export interface IWorkbenchExtensionManagementService extends IProfileAwareExten canInstall(extension: IGalleryExtension | IResourceExtension): Promise; + getInstallableServers(extension: IGalleryExtension): Promise; installVSIX(location: URI, manifest: IExtensionManifest, installOptions?: InstallOptions): Promise; installFromGallery(gallery: IGalleryExtension, installOptions?: IWorkbenchInstallOptions): Promise; installFromLocation(location: URI): Promise; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index bc0a4687128..7bc18909d7f 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -554,6 +554,14 @@ export class ExtensionManagementService extends Disposable implements IWorkbench } } + async getInstallableServers(gallery: IGalleryExtension): Promise { + const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); + if (!manifest) { + return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name)); + } + return this.getInstallableExtensionManagementServers(manifest); + } + private async uninstallExtensionFromWorkspace(extension: ILocalExtension): Promise { if (!extension.isWorkspaceScoped) { throw new Error('The extension is not a workspace extension'); @@ -606,11 +614,25 @@ export class ExtensionManagementService extends Disposable implements IWorkbench const servers: IExtensionManagementServer[] = []; - // Install everywhere if asked to install everywhere or if the extension is a language pack - if (installOptions?.installEverywhere || isLanguagePackExtension(manifest)) { + if (installOptions?.servers?.length) { + const installableServers = this.getInstallableExtensionManagementServers(manifest); + servers.push(...installOptions.servers); + for (const server of servers) { + if (!installableServers.includes(server)) { + const error = new Error(localize('cannot be installed in server', "Cannot install the '{0}' extension because it is not available in the '{1}' setup.", gallery.displayName || gallery.name, server.label)); + error.name = ExtensionManagementErrorCode.Unsupported; + throw error; + } + } + } + + // Language packs should be installed on both local and remote servers + else if (isLanguagePackExtension(manifest)) { servers.push(...this.servers.filter(server => server !== this.extensionManagementServerService.webExtensionManagementServer)); - } else { - const server = this.getExtensionManagementServerToInstall(manifest); + } + + else { + const [server] = this.getInstallableExtensionManagementServers(manifest); if (server) { servers.push(server); } @@ -633,27 +655,33 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return servers; } - private getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | null { + private getInstallableExtensionManagementServers(manifest: IExtensionManifest): IExtensionManagementServer[] { // Only local server if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer; + return [this.extensionManagementServerService.localExtensionManagementServer]; } + const servers: IExtensionManagementServer[] = []; + const extensionKind = this.extensionManifestPropertiesService.getExtensionKind(manifest); for (const kind of extensionKind) { if (kind === 'ui' && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer; + servers.push(this.extensionManagementServerService.localExtensionManagementServer); } if (kind === 'workspace' && this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer; + servers.push(this.extensionManagementServerService.remoteExtensionManagementServer); } if (kind === 'web' && this.extensionManagementServerService.webExtensionManagementServer) { - return this.extensionManagementServerService.webExtensionManagementServer; + servers.push(this.extensionManagementServerService.webExtensionManagementServer); } } - // Local server can accept any extension. So return local server if not compatible server found. - return this.extensionManagementServerService.localExtensionManagementServer; + // Local server can accept any extension. + if (this.extensionManagementServerService.localExtensionManagementServer && !servers.includes(this.extensionManagementServerService.localExtensionManagementServer)) { + servers.push(this.extensionManagementServerService.localExtensionManagementServer); + } + + return servers; } private isExtensionsSyncEnabled(): boolean { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index a914f4586d4..259de7b24b0 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -160,7 +160,7 @@ import { ILayoutOffsetInfo } from '../../../platform/layout/browser/layoutServic import { IUserDataProfile, IUserDataProfilesService, toUserDataProfile, UserDataProfilesService } from '../../../platform/userDataProfile/common/userDataProfile.js'; import { UserDataProfileService } from '../../services/userDataProfile/common/userDataProfileService.js'; import { IUserDataProfileService } from '../../services/userDataProfile/common/userDataProfile.js'; -import { EnablementState, IResourceExtension, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../services/extensionManagement/common/extensionManagement.js'; +import { EnablementState, IExtensionManagementServer, IResourceExtension, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../services/extensionManagement/common/extensionManagement.js'; import { ILocalExtension, IGalleryExtension, InstallOptions, UninstallOptions, IExtensionsControlManifest, IGalleryMetadata, IExtensionManagementParticipant, Metadata, InstallExtensionResult, InstallExtensionInfo, UninstallExtensionInfo } from '../../../platform/extensionManagement/common/extensionManagement.js'; import { Codicon } from '../../../base/common/codicons.js'; import { IRemoteExtensionsScannerService } from '../../../platform/remote/common/remoteExtensionsScanner.js'; @@ -2246,6 +2246,7 @@ export class TestWorkbenchExtensionManagementService implements IWorkbenchExtens installResourceExtension(): Promise { throw new Error('Method not implemented.'); } getExtensions(): Promise { throw new Error('Method not implemented.'); } resetPinnedStateForAllUserExtensions(pinned: boolean): Promise { throw new Error('Method not implemented.'); } + getInstallableServers(extension: IGalleryExtension): Promise { throw new Error('Method not implemented.'); } } export class TestUserDataProfileService implements IUserDataProfileService { From f990dfb385d69e035810e3bd3102d301dd2157b7 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:27:08 +0100 Subject: [PATCH 401/479] Git - expose `env` through the extension API (#236598) --- extensions/git/src/api/api1.ts | 11 ++++++++++- extensions/git/src/git.ts | 5 +++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 8cc0b99f113..e559c0cb807 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -72,7 +72,6 @@ export class ApiRepositoryUIState implements RepositoryUIState { } export class ApiRepository implements Repository { - #repository: BaseRepository; readonly rootUri: Uri; @@ -311,9 +310,19 @@ export class ApiRepository implements Repository { export class ApiGit implements Git { #model: Model; + private _env: { [key: string]: string } | undefined; + constructor(model: Model) { this.#model = model; } get path(): string { return this.#model.git.path; } + + get env(): { [key: string]: string } { + if (this._env === undefined) { + this._env = Object.freeze(this.#model.git.env); + } + + return this._env; + } } export class ApiImpl implements API { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index d6a6bd52cfc..87ea23e3a85 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -315,7 +315,7 @@ export interface IGitOptions { gitPath: string; userAgent: string; version: string; - env?: any; + env?: { [key: string]: string }; } function getGitErrorCode(stderr: string): string | undefined { @@ -369,7 +369,8 @@ export class Git { readonly path: string; readonly userAgent: string; readonly version: string; - private env: any; + readonly env: { [key: string]: string }; + private commandsToLog: string[] = []; private _onOutput = new EventEmitter(); From ffbf5a7f28058787dc6476ad436b550412fce981 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:51:06 +0100 Subject: [PATCH 402/479] Use indices for queue processing instead of Array.shift() (#236601) * Use indices instead of Array.shift() * :lipstick: --- src/vs/base/browser/ui/tree/abstractTree.ts | 8 ++++---- src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index b8144e58c23..09ce418bda9 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -3080,16 +3080,16 @@ export abstract class AbstractTree implements IDisposable } const root = this.model.getNode(); - const queue = [root]; + const stack = [root]; - while (queue.length > 0) { - const node = queue.shift()!; + while (stack.length > 0) { + const node = stack.pop()!; if (node !== root && node.collapsible) { state.expanded[getId(node.element)] = node.collapsed ? 0 : 1; } - insertInto(queue, queue.length, node.children); + insertInto(stack, stack.length, node.children); } return state; diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 9d0d2cbf4b2..dd6059051f0 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -24,7 +24,7 @@ import { isIterable } from '../../../common/types.js'; import { CancellationToken, CancellationTokenSource } from '../../../common/cancellation.js'; import { IContextViewProvider } from '../contextview/contextview.js'; import { FuzzyScore } from '../../../common/filters.js'; -import { splice } from '../../../common/arrays.js'; +import { insertInto, splice } from '../../../common/arrays.js'; import { localize } from '../../../../nls.js'; interface IAsyncDataTreeNode { @@ -1350,7 +1350,7 @@ export class AsyncDataTree implements IDisposable expanded.push(getId(node.element!.element as T)); } - stack.push(...node.children); + insertInto(stack, stack.length, node.children); } return { focus, selection, expanded, scrollTop: this.scrollTop }; From 613ad56601c62ff8fea04a62444217470fbc496d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 19 Dec 2024 17:03:58 +0100 Subject: [PATCH 403/479] code cleanup (#236595) * code cleanup * Fix CI --- .../view/inlineEdits/gutterIndicatorView.ts | 82 +++++++++++-------- .../browser/view/inlineEdits/utils.ts | 11 ++- 2 files changed, 53 insertions(+), 40 deletions(-) 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 936ab8b56b3..bcfa7d7463b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -40,21 +40,36 @@ export const inlineEditIndicatorBackground = registerColor( export class InlineEditsGutterIndicator extends Disposable { - private readonly _state = derived(reader => { - const range = mapOutFalsy(this._originalRange).read(reader); - if (!range) { - return undefined; - } + constructor( + private readonly _editorObs: ObservableCodeEditor, + private readonly _originalRange: IObservable, + private readonly _model: IObservable, + ) { + super(); + + this._register(this._editorObs.createOverlayWidget({ + domNode: this._indicator.element, + position: constObservable(null), + allowEditorOverflow: false, + minContentWidthInPx: constObservable(0), + })); + } + + private readonly _originalRangeObs = mapOutFalsy(this._originalRange); + private readonly _state = derived(reader => { + const range = this._originalRangeObs.read(reader); + if (!range) { return undefined; } return { range, lineOffsetRange: this._editorObs.observeLineOffsetRange(range, this._store), }; }); - private _stickyScrollController = StickyScrollController.get(this._editorObs.editor); - private readonly _stickyScrollHeight = this._stickyScrollController ? observableFromEvent(this._stickyScrollController.onDidChangeStickyScrollHeight, () => this._stickyScrollController!.stickyScrollWidgetHeight) : constObservable(0); - + private readonly _stickyScrollController = StickyScrollController.get(this._editorObs.editor); + private readonly _stickyScrollHeight = this._stickyScrollController + ? observableFromEvent(this._stickyScrollController.onDidChangeStickyScrollHeight, () => this._stickyScrollController!.stickyScrollWidgetHeight) + : constObservable(0); private readonly _layout = derived(reader => { const s = this._state.read(reader); @@ -85,13 +100,13 @@ export class InlineEditsGutterIndicator extends Disposable { return { rect, iconRect, - mode: (iconRect.top === targetRect.top ? 'right' as const + arrowDirection: (iconRect.top === targetRect.top ? 'right' as const : iconRect.top > targetRect.top ? 'top' as const : 'bottom' as const), docked: rect.containsRect(iconRect) && viewPortWithStickyScroll.containsRect(iconRect), }; }); - private readonly _mode = derived(this, reader => { + private readonly _tabAction = derived(this, reader => { const m = this._model.read(reader); if (m && m.tabShouldAcceptInlineEdit.read(reader)) { return 'accept' as const; } if (m && m.tabShouldJumpToInlineEdit.read(reader)) { return 'jump' as const; } @@ -135,16 +150,20 @@ export class InlineEditsGutterIndicator extends Disposable { cursor: 'pointer', zIndex: '1000', position: 'absolute', - backgroundColor: this._mode.map(v => ({ - inactive: 'var(--vscode-inlineEdit-gutterIndicator-secondaryBackground)', - jump: 'var(--vscode-inlineEdit-gutterIndicator-primaryBackground)', - accept: 'var(--vscode-inlineEdit-gutterIndicator-successfulBackground)', - }[v])), - '--vscodeIconForeground': this._mode.map(v => ({ - inactive: 'var(--vscode-inlineEdit-gutterIndicator-secondaryForeground)', - jump: 'var(--vscode-inlineEdit-gutterIndicator-primaryForeground)', - accept: 'var(--vscode-inlineEdit-gutterIndicator-successfulForeground)', - }[v])), + backgroundColor: this._tabAction.map(v => { + switch (v) { + case 'inactive': return 'var(--vscode-inlineEdit-gutterIndicator-secondaryBackground)'; + case 'jump': return 'var(--vscode-inlineEdit-gutterIndicator-primaryBackground)'; + case 'accept': return 'var(--vscode-inlineEdit-gutterIndicator-successfulBackground)'; + } + }), + '--vscodeIconForeground': this._tabAction.map(v => { + switch (v) { + case 'inactive': return 'var(--vscode-inlineEdit-gutterIndicator-secondaryForeground)'; + case 'jump': return 'var(--vscode-inlineEdit-gutterIndicator-primaryForeground)'; + case 'accept': return 'var(--vscode-inlineEdit-gutterIndicator-successfulForeground)'; + } + }), borderRadius: '4px', display: 'flex', justifyContent: 'center', @@ -154,7 +173,13 @@ export class InlineEditsGutterIndicator extends Disposable { }, [ n.div({ style: { - rotate: l.map(l => ({ right: '0deg', bottom: '90deg', top: '-90deg' }[l.mode])), + rotate: l.map(l => { + switch (l.arrowDirection) { + case 'right': return '0deg'; + case 'bottom': return '90deg'; + case 'top': return '-90deg'; + } + }), transition: 'rotate 0.2s ease-in-out', } }, [ @@ -162,21 +187,6 @@ export class InlineEditsGutterIndicator extends Disposable { ]) ]), ])).keepUpdated(this._store); - - constructor( - private readonly _editorObs: ObservableCodeEditor, - private readonly _originalRange: IObservable, - private readonly _model: IObservable, - ) { - super(); - - this._register(this._editorObs.createOverlayWidget({ - domNode: this._indicator.element, - position: constObservable(null), - allowEditorOverflow: false, - minContentWidthInPx: constObservable(0), - })); - } } function rectToProps(fn: (reader: IReader) => Rect): any { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index 73b9cbb5618..b99b0478bdc 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -423,20 +423,23 @@ type ElementAttributeKeys = Partial<{ ? never : T[K] extends object ? ElementAttributeKeys - : Value + : Value }>; -export function mapOutFalsy(obs: IObservable): IObservable | undefined | null | false> { +type RemoveFalsy = T extends false | undefined | null ? never : T; +type Falsy = T extends false | undefined | null ? T : never; + +export function mapOutFalsy(obs: IObservable): IObservable> | Falsy> { const nonUndefinedObs = derivedObservableWithCache(undefined, (reader, lastValue) => obs.read(reader) || lastValue); return derived(reader => { nonUndefinedObs.read(reader); const val = obs.read(reader); if (!val) { - return undefined; + return undefined as Falsy; } - return nonUndefinedObs as IObservable; + return nonUndefinedObs as IObservable>; }); } From 9945f3b953f1c330bc4a9b1dd02b17304d56eead Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 19 Dec 2024 10:55:15 -0600 Subject: [PATCH 404/479] Revert "focus debug console when it becomes visible " (#236607) --- src/vs/workbench/contrib/debug/browser/repl.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index a9959c5d1c5..edef7e72b70 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -214,7 +214,6 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this.tree?.updateChildren(undefined, true, false); this.onDidStyleChange(); } - this.focus(); } })); this._register(this.configurationService.onDidChangeConfiguration(e => { From ceab34bc36825ebd163e625426c2d6518c177a0b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 Dec 2024 18:10:29 +0100 Subject: [PATCH 405/479] icon themes: remove fontCharacterRegex (#236610) --- src/vs/platform/theme/common/iconRegistry.ts | 4 +--- .../services/themes/browser/fileIconThemeData.ts | 4 ++-- .../services/themes/browser/productIconThemeData.ts | 6 +++--- .../services/themes/common/fileIconThemeSchema.ts | 6 ++---- .../services/themes/common/iconExtensionPoint.ts | 10 ++-------- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/vs/platform/theme/common/iconRegistry.ts b/src/vs/platform/theme/common/iconRegistry.ts index 775c1cf1d4a..88cee9c3ddb 100644 --- a/src/vs/platform/theme/common/iconRegistry.ts +++ b/src/vs/platform/theme/common/iconRegistry.ts @@ -152,10 +152,8 @@ export const fontStyleRegex = /^(normal|italic|(oblique[ \w\s-]+))$/; export const fontWeightRegex = /^(normal|bold|lighter|bolder|(\d{0-1000}))$/; export const fontSizeRegex = /^([\w_.%+-]+)$/; export const fontFormatRegex = /^woff|woff2|truetype|opentype|embedded-opentype|svg$/; -export const fontCharacterRegex = /^([^\\]|\\[a-fA-F0-9]+)$/u; export const fontColorRegex = /^#[0-9a-fA-F]{0,6}$/; -export const fontCharacterErrorMessage = localize('schema.fontCharacter.formatError', 'The fontCharacter must be a single letter or a backslash followed by unicode code points in hexadecimal.'); export const fontIdErrorMessage = localize('schema.fontId.formatError', 'The font ID must only contain letters, numbers, underscores and dashes.'); class IconRegistry implements IIconRegistry { @@ -170,7 +168,7 @@ class IconRegistry implements IIconRegistry { type: 'object', properties: { fontId: { type: 'string', description: localize('iconDefinition.fontId', 'The id of the font to use. If not set, the font that is defined first is used.'), pattern: fontIdRegex.source, patternErrorMessage: fontIdErrorMessage }, - fontCharacter: { type: 'string', description: localize('iconDefinition.fontCharacter', 'The font character associated with the icon definition.'), pattern: fontCharacterRegex.source, patternErrorMessage: fontCharacterErrorMessage } + fontCharacter: { type: 'string', description: localize('iconDefinition.fontCharacter', 'The font character associated with the icon definition.') } }, additionalProperties: false, defaultSnippets: [{ body: { fontCharacter: '\\\\e030' } }] diff --git a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts index e1d349be481..a6e4c965f49 100644 --- a/src/vs/workbench/services/themes/browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/fileIconThemeData.ts @@ -13,7 +13,7 @@ import { getParseErrorMessage } from '../../../../base/common/jsonErrorMessages. import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IExtensionResourceLoaderService } from '../../../../platform/extensionResourceLoader/common/extensionResourceLoader.js'; import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { fontCharacterRegex, fontColorRegex, fontSizeRegex } from '../../../../platform/theme/common/iconRegistry.js'; +import { fontColorRegex, fontSizeRegex } from '../../../../platform/theme/common/iconRegistry.js'; import * as css from '../../../../base/browser/cssValue.js'; import { fileIconSelectorEscape } from '../../../../editor/common/services/getIconClasses.js'; @@ -424,7 +424,7 @@ export class FileIconThemeLoader { if (definition.fontColor && definition.fontColor.match(fontColorRegex)) { body.push(css.inline`color: ${css.hexColorValue(definition.fontColor)};`); } - if (definition.fontCharacter && definition.fontCharacter.match(fontCharacterRegex)) { + if (definition.fontCharacter) { body.push(css.inline`content: ${css.stringValue(definition.fontCharacter)};`); } const fontSize = definition.fontSize ?? (definition.fontId ? fontSizes.get(definition.fontId) : undefined); diff --git a/src/vs/workbench/services/themes/browser/productIconThemeData.ts b/src/vs/workbench/services/themes/browser/productIconThemeData.ts index 1e0a9fa295c..824a35ca9e9 100644 --- a/src/vs/workbench/services/themes/browser/productIconThemeData.ts +++ b/src/vs/workbench/services/themes/browser/productIconThemeData.ts @@ -13,7 +13,7 @@ import { getParseErrorMessage } from '../../../../base/common/jsonErrorMessages. import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { isObject, isString } from '../../../../base/common/types.js'; import { ILogService } from '../../../../platform/log/common/log.js'; -import { IconDefinition, getIconRegistry, IconContribution, IconFontDefinition, IconFontSource, fontIdRegex, fontWeightRegex, fontStyleRegex, fontFormatRegex, fontCharacterRegex, fontCharacterErrorMessage } from '../../../../platform/theme/common/iconRegistry.js'; +import { IconDefinition, getIconRegistry, IconContribution, IconFontDefinition, IconFontSource, fontIdRegex, fontWeightRegex, fontStyleRegex, fontFormatRegex } from '../../../../platform/theme/common/iconRegistry.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IExtensionResourceLoaderService } from '../../../../platform/extensionResourceLoader/common/extensionResourceLoader.js'; @@ -244,7 +244,7 @@ function _loadProductIconThemeDocument(fileService: IExtensionResourceLoaderServ for (const iconId in contentValue.iconDefinitions) { const definition = contentValue.iconDefinitions[iconId]; - if (isString(definition.fontCharacter) && definition.fontCharacter.match(fontCharacterRegex)) { + if (isString(definition.fontCharacter)) { const fontId = definition.fontId ?? primaryFontId; const fontDefinition = sanitizedFonts.get(fontId); if (fontDefinition) { @@ -255,7 +255,7 @@ function _loadProductIconThemeDocument(fileService: IExtensionResourceLoaderServ warnings.push(nls.localize('error.icon.font', 'Skipping icon definition \'{0}\'. Unknown font.', iconId)); } } else { - warnings.push(nls.localize('error.icon.fontCharacter', 'Skipping icon definition \'{0}\': {1}', iconId, fontCharacterErrorMessage)); + warnings.push(nls.localize('error.icon.fontCharacter', 'Skipping icon definition \'{0}\': Needs to be defined', iconId)); } } return { iconDefinitions }; diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index f51191117c5..6a8ece75403 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -7,7 +7,7 @@ import * as nls from '../../../../nls.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { Extensions as JSONExtensions, IJSONContributionRegistry } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; -import { fontWeightRegex, fontStyleRegex, fontSizeRegex, fontIdRegex, fontCharacterRegex, fontColorRegex, fontCharacterErrorMessage, fontIdErrorMessage } from '../../../../platform/theme/common/iconRegistry.js'; +import { fontWeightRegex, fontStyleRegex, fontSizeRegex, fontIdRegex, fontColorRegex, fontIdErrorMessage } from '../../../../platform/theme/common/iconRegistry.js'; const schemaId = 'vscode://schemas/icon-theme'; const schema: IJSONSchema = { @@ -208,9 +208,7 @@ const schema: IJSONSchema = { }, fontCharacter: { type: 'string', - description: nls.localize('schema.fontCharacter', 'When using a glyph font: The character in the font to use.'), - pattern: fontCharacterRegex.source, - patternErrorMessage: fontCharacterErrorMessage + description: nls.localize('schema.fontCharacter', 'When using a glyph font: The character in the font to use.') }, fontColor: { type: 'string', diff --git a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts index 9b554226c3f..08a9b059113 100644 --- a/src/vs/workbench/services/themes/common/iconExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/iconExtensionPoint.ts @@ -5,7 +5,7 @@ import * as nls from '../../../../nls.js'; import { ExtensionsRegistry } from '../../extensions/common/extensionsRegistry.js'; -import { IIconRegistry, Extensions as IconRegistryExtensions, fontCharacterErrorMessage, fontCharacterRegex } from '../../../../platform/theme/common/iconRegistry.js'; +import { IIconRegistry, Extensions as IconRegistryExtensions } from '../../../../platform/theme/common/iconRegistry.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import * as resources from '../../../../base/common/resources.js'; @@ -53,9 +53,7 @@ const iconConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint Date: Thu, 19 Dec 2024 19:15:05 +0100 Subject: [PATCH 406/479] Implements experimental word replacement and insertion views. (#236618) --- src/vs/editor/browser/rect.ts | 3 +- .../diffEditorViewZones/renderLines.ts | 33 +- src/vs/editor/common/config/editorOptions.ts | 19 ++ src/vs/editor/common/core/range.ts | 4 + .../defaultLinesDiffComputer.ts | 19 +- .../heuristicSequenceOptimizations.ts | 18 +- .../linesSliceCharSequence.ts | 29 ++ .../common/diff/documentDiffProvider.ts | 2 + .../editor/common/diff/linesDiffComputer.ts | 1 + .../view/inlineEdits/gutterIndicatorView.ts | 15 +- .../browser/view/inlineEdits/utils.ts | 14 + .../browser/view/inlineEdits/view.ts | 69 +++-- .../view/inlineEdits/viewAndDiffProducer.ts | 1 + .../view/inlineEdits/wordReplacementView.ts | 285 ++++++++++++++++++ src/vs/monaco.d.ts | 3 + 15 files changed, 467 insertions(+), 48 deletions(-) create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts diff --git a/src/vs/editor/browser/rect.ts b/src/vs/editor/browser/rect.ts index b990944eb67..6de464d201a 100644 --- a/src/vs/editor/browser/rect.ts +++ b/src/vs/editor/browser/rect.ts @@ -3,6 +3,7 @@ * 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 { Point } from './point.js'; @@ -49,7 +50,7 @@ export class Rect { public readonly bottom: number, ) { if (left > right || top > bottom) { - throw new Error('Invalid arguments'); + throw new BugIndicatingError('Invalid arguments'); } } diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts index d2ff0bf9fb8..ce0eea0bcaa 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts @@ -17,7 +17,7 @@ import { InlineDecoration, ViewLineRenderingData } from '../../../../../common/v const ttPolicy = createTrustedTypesPolicy('diffEditorWidget', { createHTML: value => value }); -export function renderLines(source: LineSource, options: RenderOptions, decorations: InlineDecoration[], domNode: HTMLElement): RenderLinesResult { +export function renderLines(source: LineSource, options: RenderOptions, decorations: InlineDecoration[], domNode: HTMLElement, noExtra = false): RenderLinesResult { applyFontInfo(domNode, options.fontInfo); const hasCharChanges = (decorations.length > 0); @@ -44,7 +44,8 @@ export function renderLines(source: LineSource, options: RenderOptions, decorati source.mightContainNonBasicASCII, source.mightContainRTL, options, - sb + sb, + noExtra, )); renderedLineCount++; lastBreakOffset = breakOffset; @@ -61,6 +62,7 @@ export function renderLines(source: LineSource, options: RenderOptions, decorati source.mightContainRTL, options, sb, + noExtra, )); renderedLineCount++; } @@ -125,7 +127,25 @@ export class RenderOptions { public readonly renderWhitespace: FindComputedEditorOptionValueById, public readonly renderControlCharacters: boolean, public readonly fontLigatures: FindComputedEditorOptionValueById, + public readonly setWidth = true, ) { } + + public withSetWidth(setWidth: boolean): RenderOptions { + return new RenderOptions( + this.tabSize, + this.fontInfo, + this.disableMonospaceOptimizations, + this.typicalHalfwidthCharacterWidth, + this.scrollBeyondLastColumn, + this.lineHeight, + this.lineDecorationsWidth, + this.stopRenderingLineAfter, + this.renderWhitespace, + this.renderControlCharacters, + this.fontLigatures, + setWidth, + ); + } } export interface RenderLinesResult { @@ -143,16 +163,21 @@ function renderOriginalLine( mightContainRTL: boolean, options: RenderOptions, sb: StringBuilder, + noExtra: boolean, ): number { sb.appendString('

'); + if (options.setWidth) { + sb.appendString('px;width:1000000px;">'); + } else { + sb.appendString('px;">'); + } const lineContent = lineTokens.getLineContent(); const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, mightContainNonBasicASCII); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index c7481985f2f..e355a960db9 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4197,6 +4197,9 @@ export interface IInlineSuggestOptions { enabled?: boolean; useMixedLinesDiff?: 'never' | 'whenPossible' | 'forStableInsertions' | 'afterJumpWhenPossible'; useInterleavedLinesDiff?: 'never' | 'always' | 'afterJump'; + useWordInsertionView?: 'never' | 'whenPossible'; + useWordReplacementView?: 'never' | 'whenPossible'; + onlyShowWhenCloseToCursor?: boolean; useGutterIndicator?: boolean; }; @@ -4230,6 +4233,8 @@ class InlineEditorSuggest extends BaseEditorOption seq.findWordContaining(idx)); if (check) { SequenceDiff.assertSorted(diffs); } + + if (options.extendToSubwords) { + diffs = extendDiffsToEntireWordIfAppropriate(slice1, slice2, diffs, (seq, idx) => seq.findSubWordContaining(idx), true); + if (check) { SequenceDiff.assertSorted(diffs); } + } + diffs = removeShortMatches(slice1, slice2, diffs); if (check) { SequenceDiff.assertSorted(diffs); } diffs = removeVeryShortMatchingTextBetweenLongDiffs(slice1, slice2, diffs); diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts index c205457ac53..20de8dd1ab0 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts @@ -219,7 +219,13 @@ export function removeShortMatches(sequence1: ISequence, sequence2: ISequence, s return result; } -export function extendDiffsToEntireWordIfAppropriate(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +export function extendDiffsToEntireWordIfAppropriate( + sequence1: LinesSliceCharSequence, + sequence2: LinesSliceCharSequence, + sequenceDiffs: SequenceDiff[], + findParent: (seq: LinesSliceCharSequence, idx: number) => OffsetRange | undefined, + force: boolean = false, +): SequenceDiff[] { const equalMappings = SequenceDiff.invert(sequenceDiffs, sequence1.length); const additional: SequenceDiff[] = []; @@ -231,8 +237,8 @@ export function extendDiffsToEntireWordIfAppropriate(sequence1: LinesSliceCharSe return; } - const w1 = sequence1.findWordContaining(pair.offset1); - const w2 = sequence2.findWordContaining(pair.offset2); + const w1 = findParent(sequence1, pair.offset1); + const w2 = findParent(sequence2, pair.offset2); if (!w1 || !w2) { return; } @@ -252,8 +258,8 @@ export function extendDiffsToEntireWordIfAppropriate(sequence1: LinesSliceCharSe break; } - const v1 = sequence1.findWordContaining(next.seq1Range.start); - const v2 = sequence2.findWordContaining(next.seq2Range.start); + const v1 = findParent(sequence1, next.seq1Range.start); + const v2 = findParent(sequence2, next.seq2Range.start); // Because there is an intersection, we know that the words are not empty. const v = new SequenceDiff(v1!, v2!); const equalPart = v.intersect(next)!; @@ -271,7 +277,7 @@ export function extendDiffsToEntireWordIfAppropriate(sequence1: LinesSliceCharSe } } - if (equalChars1 + equalChars2 < (w.seq1Range.length + w.seq2Range.length) * 2 / 3) { + if ((force && equalChars1 + equalChars2 < w.seq1Range.length + w.seq2Range.length) || equalChars1 + equalChars2 < (w.seq1Range.length + w.seq2Range.length) * 2 / 3) { additional.push(w); } diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts index b56245bbc0f..25b4a6127f8 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts @@ -144,6 +144,31 @@ export class LinesSliceCharSequence implements ISequence { return new OffsetRange(start, end); } + /** fooBar has the two sub-words foo and bar */ + public findSubWordContaining(offset: number): OffsetRange | undefined { + if (offset < 0 || offset >= this.elements.length) { + return undefined; + } + + if (!isWordChar(this.elements[offset])) { + return undefined; + } + + // find start + let start = offset; + while (start > 0 && isWordChar(this.elements[start - 1]) && !isUpperCase(this.elements[start])) { + start--; + } + + // find end + let end = offset; + while (end < this.elements.length && isWordChar(this.elements[end]) && !isUpperCase(this.elements[end])) { + end++; + } + + return new OffsetRange(start, end); + } + public countLinesIn(range: OffsetRange): number { return this.translateOffset(range.endExclusive).lineNumber - this.translateOffset(range.start).lineNumber; } @@ -165,6 +190,10 @@ function isWordChar(charCode: number): boolean { || charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9; } +function isUpperCase(charCode: number): boolean { + return charCode >= CharCode.A && charCode <= CharCode.Z; +} + const enum CharBoundaryCategory { WordLower, WordUpper, diff --git a/src/vs/editor/common/diff/documentDiffProvider.ts b/src/vs/editor/common/diff/documentDiffProvider.ts index da88be843cb..6f6f06a9d73 100644 --- a/src/vs/editor/common/diff/documentDiffProvider.ts +++ b/src/vs/editor/common/diff/documentDiffProvider.ts @@ -45,6 +45,8 @@ export interface IDocumentDiffProviderOptions { * If set, the diff computation should compute moves in addition to insertions and deletions. */ computeMoves: boolean; + + extendToSubwords?: boolean; } /** diff --git a/src/vs/editor/common/diff/linesDiffComputer.ts b/src/vs/editor/common/diff/linesDiffComputer.ts index 054d1eebfdd..6a384b6d8a5 100644 --- a/src/vs/editor/common/diff/linesDiffComputer.ts +++ b/src/vs/editor/common/diff/linesDiffComputer.ts @@ -13,6 +13,7 @@ export interface ILinesDiffComputerOptions { readonly ignoreTrimWhitespace: boolean; readonly maxComputationTimeMs: number; readonly computeMoves: boolean; + readonly extendToSubwords?: boolean; } export class LinesDiff { 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 bcfa7d7463b..b0f8b3dc54d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -6,7 +6,7 @@ import { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IObservable, IReader, constObservable, derived, observableFromEvent } from '../../../../../../base/common/observable.js'; +import { IObservable, constObservable, derived, observableFromEvent } from '../../../../../../base/common/observable.js'; import { buttonBackground, buttonForeground, buttonSecondaryBackground, buttonSecondaryForeground } from '../../../../../../platform/theme/common/colorRegistry.js'; import { registerColor, transparent } from '../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; @@ -16,7 +16,7 @@ import { LineRange } from '../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../common/core/offsetRange.js'; import { StickyScrollController } from '../../../../stickyScroll/browser/stickyScrollController.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; -import { mapOutFalsy, n } from './utils.js'; +import { mapOutFalsy, n, rectToProps } from './utils.js'; export const inlineEditIndicatorPrimaryForeground = registerColor('inlineEdit.gutterIndicator.primaryForeground', buttonForeground, 'Foreground color for the primary inline edit gutter indicator.'); export const inlineEditIndicatorPrimaryBackground = registerColor('inlineEdit.gutterIndicator.primaryBackground', buttonBackground, 'Background color for the primary inline edit gutter indicator.'); @@ -157,7 +157,7 @@ export class InlineEditsGutterIndicator extends Disposable { case 'accept': return 'var(--vscode-inlineEdit-gutterIndicator-successfulBackground)'; } }), - '--vscodeIconForeground': this._tabAction.map(v => { + ['--vscodeIconForeground' as any]: this._tabAction.map(v => { switch (v) { case 'inactive': return 'var(--vscode-inlineEdit-gutterIndicator-secondaryForeground)'; case 'jump': return 'var(--vscode-inlineEdit-gutterIndicator-primaryForeground)'; @@ -188,12 +188,3 @@ export class InlineEditsGutterIndicator extends Disposable { ]), ])).keepUpdated(this._store); } - -function rectToProps(fn: (reader: IReader) => Rect): any { - return { - left: derived(reader => fn(reader).left), - top: derived(reader => fn(reader).top), - width: derived(reader => fn(reader).right - fn(reader).left), - height: derived(reader => fn(reader).bottom - fn(reader).top), - }; -} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index b99b0478bdc..a70a0721491 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -16,6 +16,7 @@ import { URI } from '../../../../../../base/common/uri.js'; import { MenuEntryActionViewItem } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { Point } from '../../../../../browser/point.js'; +import { Rect } from '../../../../../browser/rect.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../common/core/offsetRange.js'; @@ -173,9 +174,13 @@ type SVGElementTagNameMap2 = { width: number; height: number; transform: string; + viewBox: string; + fill: string; }; path: SVGElement & { d: string; + stroke: string; + fill: string; }; linearGradient: SVGElement & { id: string; @@ -465,3 +470,12 @@ export function observeElementPosition(element: HTMLElement, store: DisposableSt left }; } + +export function rectToProps(fn: (reader: IReader) => Rect) { + return { + left: derived(reader => fn(reader).left), + top: derived(reader => fn(reader).top), + width: derived(reader => fn(reader).right - fn(reader).left), + height: derived(reader => fn(reader).bottom - fn(reader).top), + }; +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts index 68bc963e374..dfb11c8bebf 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { autorunWithStore, derived, IObservable, IReader } from '../../../../../../base/common/observable.js'; +import { autorunWithStore, derived, IObservable, IReader, mapObservableArrayCached } from '../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../common/core/lineRange.js'; import { Position } from '../../../../../common/core/position.js'; -import { StringText } from '../../../../../common/core/textEdit.js'; +import { SingleTextEdit, StringText } from '../../../../../common/core/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'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; @@ -22,12 +23,15 @@ import { InlineEditsSideBySideDiff } from './sideBySideDiff.js'; import { applyEditToModifiedRangeMappings, createReindentEdit } from './utils.js'; import './view.css'; import { InlineEditWithChanges } from './viewAndDiffProducer.js'; +import { WordInsertView, WordReplacementView } from './wordReplacementView.js'; export class InlineEditsView extends Disposable { private readonly _editorObs = observableCodeEditor(this._editor); private readonly _useMixedLinesDiff = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useMixedLinesDiff); private readonly _useInterleavedLinesDiff = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useInterleavedLinesDiff); + private readonly _useWordReplacementView = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useWordReplacementView); + private readonly _useWordInsertionView = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useWordInsertionView); constructor( private readonly _editor: ICodeEditor, @@ -39,7 +43,7 @@ export class InlineEditsView extends Disposable { } private readonly _uiState = derived<{ - state: 'collapsed' | 'mixedLines' | 'ghostText' | 'interleavedLines' | 'sideBySide'; + state: ReturnType; diff: DetailedLineRangeMapping[]; edit: InlineEditWithChanges; newText: string; @@ -57,9 +61,9 @@ export class InlineEditsView extends Disposable { let newText = edit.edit.apply(edit.originalText); let diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); - const state = this.determinRenderState(edit, reader, diff); + const state = this.determineRenderState(edit, reader, diff, new StringText(newText)); - if (state === 'sideBySide') { + if (state.kind === 'sideBySide') { const indentationAdjustmentEdit = createReindentEdit(newText, edit.modifiedLineRange); newText = indentationAdjustmentEdit.applyToString(newText); @@ -98,7 +102,7 @@ export class InlineEditsView extends Disposable { this._editor, this._edit, this._previewTextModel, - this._uiState.map(s => s && s.state === 'sideBySide' ? ({ + this._uiState.map(s => s && s.state.kind === 'sideBySide' ? ({ edit: s.edit, newTextLineCount: s.newTextLineCount, originalDisplayRange: s.originalDisplayRange, @@ -108,17 +112,27 @@ export class InlineEditsView extends Disposable { private readonly _inlineDiffViewState = derived(this, reader => { const e = this._uiState.read(reader); if (!e) { return undefined; } - + if (e.state.kind === 'wordReplacements') { + return undefined; + } return { modifiedText: new StringText(e.newText), diff: e.diff, - mode: e.state === 'collapsed' ? 'sideBySide' : e.state, + mode: e.state.kind === 'collapsed' ? 'sideBySide' : e.state.kind, modifiedCodeEditor: this._sideBySide.previewEditor, }; }); protected readonly _inlineDiffView = this._register(new OriginalEditorInlineDiffView(this._editor, this._inlineDiffViewState, this._previewTextModel)); + protected readonly _wordReplacementViews = mapObservableArrayCached(this, this._uiState.map(s => s?.state.kind === 'wordReplacements' ? s.state.replacements : []), (e, store) => { + if (e.range.isEmpty()) { + return store.add(this._instantiationService.createInstance(WordInsertView, this._editorObs, e)); + } else { + return store.add(this._instantiationService.createInstance(WordReplacementView, this._editorObs, e)); + } + }).recomputeInitiallyAndOnChange(this._store); + private readonly _useGutterIndicator = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.experimental.useGutterIndicator); protected readonly _indicator = this._register(autorunWithStore((reader, store) => { @@ -136,37 +150,54 @@ export class InlineEditsView extends Disposable { if (!state) { return undefined; } const range = state.originalDisplayRange; const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader); - return { editTop: top, showAlways: state.state !== 'sideBySide' }; + return { editTop: top, showAlways: state.state.kind !== 'sideBySide' }; }), this._model, )); } })); - private determinRenderState(edit: InlineEditWithChanges, reader: IReader, diff: DetailedLineRangeMapping[]) { + private determineRenderState(edit: InlineEditWithChanges, reader: IReader, diff: DetailedLineRangeMapping[], newText: StringText) { if (edit.isCollapsed) { - return 'collapsed'; + return { kind: 'collapsed' as const }; } if ( - (this._useMixedLinesDiff.read(reader) === 'whenPossible' || (edit.userJumpedToIt && this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible')) - && diff.every(m => OriginalEditorInlineDiffView.supportsInlineDiffRendering(m)) + this._useMixedLinesDiff.read(reader) === 'forStableInsertions' + && isInsertionAfterPosition(diff, edit.cursorPosition) ) { - return 'mixedLines'; + return { kind: 'ghostText' as const }; + } + + if (diff.length === 1 && diff[0].original.length === 1 && diff[0].modified.length === 1) { + const inner = diff.flatMap(d => d.innerChanges!); + if (inner.every( + m => (m.originalRange.isEmpty() && this._useWordInsertionView.read(reader) === 'whenPossible' + || !m.originalRange.isEmpty() && this._useWordReplacementView.read(reader) === 'whenPossible') + && TextLength.ofRange(m.originalRange).columnCount < 100 + && TextLength.ofRange(m.modifiedRange).columnCount < 100 + )) { + return { + kind: 'wordReplacements' as const, + replacements: inner.map(i => + new SingleTextEdit(i.originalRange, newText.getValueOfRange(i.modifiedRange)) + ) + }; + } } if ( - this._useMixedLinesDiff.read(reader) === 'forStableInsertions' - && isInsertionAfterPosition(diff, edit.cursorPosition) + (this._useMixedLinesDiff.read(reader) === 'whenPossible' || (edit.userJumpedToIt && this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible')) + && diff.every(m => OriginalEditorInlineDiffView.supportsInlineDiffRendering(m)) ) { - return 'ghostText'; + return { kind: 'mixedLines' as const }; } if (this._useInterleavedLinesDiff.read(reader) === 'always' || (edit.userJumpedToIt && this._useInterleavedLinesDiff.read(reader) === 'afterJump')) { - return 'interleavedLines'; + return { kind: 'interleavedLines' as const }; } - return 'sideBySide'; + return { kind: 'sideBySide' as const }; } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts index ed40c6c0edb..f56b4d2c986 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts @@ -47,6 +47,7 @@ export class InlineEditsViewAndDiffProducer extends Disposable { computeMoves: false, ignoreTrimWhitespace: false, maxComputationTimeMs: 1000, + extendToSubwords: true, }, CancellationToken.None); return result; }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts new file mode 100644 index 00000000000..6e6f213d38a --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts @@ -0,0 +1,285 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { constObservable, derived } from '../../../../../../base/common/observable.js'; +import { editorHoverStatusBarBackground } from '../../../../../../platform/theme/common/colorRegistry.js'; +import { registerColor, transparent } 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 { 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 { OffsetRange } from '../../../../../common/core/offsetRange.js'; +import { SingleTextEdit } from '../../../../../common/core/textEdit.js'; +import { ILanguageService } from '../../../../../common/languages/language.js'; +import { LineTokens } from '../../../../../common/tokens/lineTokens.js'; +import { TokenArray } from '../../../../../common/tokens/tokenArray.js'; +import { mapOutFalsy, n, rectToProps } from './utils.js'; + +export const transparentHoverBackground = registerColor( + 'inlineEdit.wordReplacementView.background', + { + light: transparent(editorHoverStatusBarBackground, 0.1), + dark: transparent(editorHoverStatusBarBackground, 0.5), + hcLight: transparent(editorHoverStatusBarBackground, 0.1), + hcDark: transparent(editorHoverStatusBarBackground, 0.1), + }, + 'Background color for the inline edit word replacement view.' +); + +export class WordReplacementView extends Disposable { + private readonly _start = this._editor.observePosition(constObservable(this._edit.range.getStartPosition()), this._store); + private readonly _end = this._editor.observePosition(constObservable(this._edit.range.getEndPosition()), this._store); + + private readonly _line = document.createElement('div'); + + private readonly _text = derived(reader => { + const tm = this._editor.model.get()!; + const origLine = tm.getLineContent(this._edit.range.startLineNumber); + + const edit = SingleOffsetEdit.replace(new OffsetRange(this._edit.range.startColumn - 1, this._edit.range.endColumn - 1), this._edit.text); + const lineToTokenize = edit.apply(origLine); + const t = tm.tokenization.tokenizeLinesAt(this._edit.range.startLineNumber, [lineToTokenize])?.[0]; + let tokens: LineTokens; + if (t) { + tokens = TokenArray.fromLineTokens(t).slice(edit.getRangeAfterApply()).toLineTokens(this._edit.text, this._languageService.languageIdCodec); + } else { + tokens = LineTokens.createEmpty(this._edit.text, this._languageService.languageIdCodec); + } + renderLines(new LineSource([tokens]), RenderOptions.fromEditor(this._editor.editor).withSetWidth(false), [], this._line, true); + }); + + private readonly _layout = derived(this, reader => { + this._text.read(reader); + const start = this._start.read(reader); + const end = this._end.read(reader); + if (!start || !end) { + return undefined; + } + const contentLeft = this._editor.layoutInfoContentLeft.read(reader); + const lineHeight = this._editor.getOption(EditorOption.lineHeight).read(reader); + if (start.x > end.x) { + return undefined; + } + const original = Rect.fromLeftTopWidthHeight(start.x + contentLeft - this._editor.scrollLeft.read(reader), start.y, end.x - start.x, lineHeight); + const w = this._editor.getOption(EditorOption.fontInfo).read(reader).typicalHalfwidthCharacterWidth; + const modified = Rect.fromLeftTopWidthHeight(original.left + 20, original.top + lineHeight + 5, this._edit.text.length * w + 5, original.height); + const background = Rect.hull([original, modified]).withMargin(4); + + return { + original, + modified, + background, + lowerBackground: background.intersectVertical(new OffsetRange(original.bottom, Number.MAX_SAFE_INTEGER)), + }; + }); + + + + private readonly _div = n.div({ + class: 'word-replacement', + }, [ + derived(reader => { + const layout = mapOutFalsy(this._layout).read(reader); + if (!layout) { + return []; + } + + return [ + n.div({ + style: { + position: 'absolute', + ...rectToProps(reader => layout.read(reader).lowerBackground), + borderRadius: '4px', + background: 'var(--vscode-editor-background)' + } + }, []), + n.div({ + style: { + position: 'absolute', + ...rectToProps(reader => layout.read(reader).modified), + borderRadius: '4px', + padding: '0px', + textAlign: 'center', + background: 'var(--vscode-inlineEdit-modifiedChangedTextBackground)', + fontFamily: this._editor.getOption(EditorOption.fontFamily), + fontSize: this._editor.getOption(EditorOption.fontSize), + fontWeight: this._editor.getOption(EditorOption.fontWeight), + } + }, [ + this._line, + ]), + n.div({ + style: { + position: 'absolute', + ...rectToProps(reader => layout.read(reader).original), + borderRadius: '4px', + boxSizing: 'border-box', + background: 'var(--vscode-inlineEdit-originalChangedTextBackground)', + } + }, []), + n.div({ + style: { + position: 'absolute', + ...rectToProps(reader => layout.read(reader).background), + borderRadius: '4px', + + border: '1px solid var(--vscode-editorHoverWidget-border)', + //background: 'rgba(122, 122, 122, 0.12)', looks better + background: 'var(--vscode-inlineEdit-wordReplacementView-background)', + } + }, []), + + n.svg({ + width: 11, + height: 13, + viewBox: '0 0 11 13', + fill: 'none', + style: { + position: 'absolute', + left: derived(reader => layout.read(reader).modified.left - 15), + top: derived(reader => layout.read(reader).modified.top), + } + }, [ + n.svgElem('path', { + d: 'M1 0C1 2.98966 1 4.92087 1 7.49952C1 8.60409 1.89543 9.5 3 9.5H10.5', + stroke: 'var(--vscode-editorHoverWidget-foreground)', + }), + n.svgElem('path', { + d: 'M6 6.5L9.99999 9.49998L6 12.5', + stroke: 'var(--vscode-editorHoverWidget-foreground)', + }) + ]), + + ]; + }) + ]).keepUpdated(this._store); + + constructor( + private readonly _editor: ObservableCodeEditor, + /** Must be single-line in both sides */ + private readonly _edit: SingleTextEdit, + @ILanguageService private readonly _languageService: ILanguageService, + ) { + super(); + + this._register(this._editor.createOverlayWidget({ + domNode: this._div.element, + minContentWidthInPx: constObservable(0), + position: constObservable({ preference: { top: 0, left: 0 } }), + allowEditorOverflow: false, + })); + } +} + +export class WordInsertView extends Disposable { + private readonly _start = this._editor.observePosition(constObservable(this._edit.range.getStartPosition()), this._store); + + private readonly _layout = derived(this, reader => { + const start = this._start.read(reader); + if (!start) { + return undefined; + } + const contentLeft = this._editor.layoutInfoContentLeft.read(reader); + const lineHeight = this._editor.getOption(EditorOption.lineHeight).read(reader); + + const w = this._editor.getOption(EditorOption.fontInfo).read(reader).typicalHalfwidthCharacterWidth; + const width = this._edit.text.length * w + 5; + + const center = new Point(contentLeft + start.x + w / 2 - this._editor.scrollLeft.read(reader), start.y); + + const modified = Rect.fromLeftTopWidthHeight(center.x - width / 2, center.y + lineHeight + 5, width, lineHeight); + const background = Rect.hull([Rect.fromPoint(center), modified]).withMargin(4); + + return { + modified, + center, + background, + lowerBackground: background.intersectVertical(new OffsetRange(modified.top - 2, Number.MAX_SAFE_INTEGER)), + }; + }); + + private readonly _div = n.div({ + class: 'word-insert', + }, [ + derived(reader => { + const layout = mapOutFalsy(this._layout).read(reader); + if (!layout) { + return []; + } + + return [ + n.div({ + style: { + position: 'absolute', + ...rectToProps(reader => layout.read(reader).lowerBackground), + borderRadius: '4px', + background: 'var(--vscode-editor-background)' + } + }, []), + n.div({ + style: { + position: 'absolute', + ...rectToProps(reader => layout.read(reader).modified), + borderRadius: '4px', + padding: '0px', + textAlign: 'center', + background: 'var(--vscode-inlineEdit-modifiedChangedTextBackground)', + fontFamily: this._editor.getOption(EditorOption.fontFamily), + fontSize: this._editor.getOption(EditorOption.fontSize), + fontWeight: this._editor.getOption(EditorOption.fontWeight), + } + }, [ + this._edit.text, + ]), + n.div({ + style: { + position: 'absolute', + ...rectToProps(reader => layout.read(reader).background), + borderRadius: '4px', + border: '1px solid var(--vscode-editorHoverWidget-border)', + //background: 'rgba(122, 122, 122, 0.12)', looks better + background: 'var(--vscode-inlineEdit-wordReplacementView-background)', + } + }, []), + n.svg({ + viewBox: '0 0 12 18', + width: 12, + height: 18, + fill: 'none', + style: { + position: 'absolute', + left: derived(reader => layout.read(reader).center.x - 9), + top: derived(reader => layout.read(reader).center.y + 4), + transform: 'scale(1.4, 1.4)', + } + }, [ + n.svgElem('path', { + d: 'M5.06445 0H7.35759C7.35759 0 7.35759 8.47059 7.35759 11.1176C7.35759 13.7647 9.4552 18 13.4674 18C17.4795 18 -2.58445 18 0.281373 18C3.14719 18 5.06477 14.2941 5.06477 11.1176C5.06477 7.94118 5.06445 0 5.06445 0Z', + fill: 'var(--vscode-inlineEdit-modifiedChangedTextBackground)', + }) + ]) + + ]; + }) + ]).keepUpdated(this._store); + + constructor( + private readonly _editor: ObservableCodeEditor, + /** Must be single-line in both sides */ + private readonly _edit: SingleTextEdit, + ) { + super(); + + this._register(this._editor.createOverlayWidget({ + domNode: this._div.element, + minContentWidthInPx: constObservable(0), + position: constObservable({ preference: { top: 0, left: 0 } }), + allowEditorOverflow: false, + })); + } +} diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 7b6d78e8552..c1df5f99ffe 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -773,6 +773,7 @@ declare namespace monaco { * Moves the range by the given amount of lines. */ delta(lineCount: number): Range; + isSingleLine(): boolean; static fromPositions(start: IPosition, end?: IPosition): Range; /** * Create a `Range` from an `IRange`. @@ -4604,6 +4605,8 @@ declare namespace monaco.editor { enabled?: boolean; useMixedLinesDiff?: 'never' | 'whenPossible' | 'forStableInsertions' | 'afterJumpWhenPossible'; useInterleavedLinesDiff?: 'never' | 'always' | 'afterJump'; + useWordInsertionView?: 'never' | 'whenPossible'; + useWordReplacementView?: 'never' | 'whenPossible'; onlyShowWhenCloseToCursor?: boolean; useGutterIndicator?: boolean; }; From a6210a08ac0156635424b396625d52256ac188fc Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 19 Dec 2024 12:18:50 -0600 Subject: [PATCH 407/479] user aria alert vs native when navigating in terminal accessible view (#236622) fix #236621 --- .../accessibility/browser/terminal.accessibility.contribution.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 5c69ee4bae9..b0f68a6eecf 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -33,6 +33,7 @@ import { BufferContentTracker } from './bufferContentTracker.js'; import { TerminalAccessibilityHelpProvider } from './terminalAccessibilityHelp.js'; import { ICommandWithEditorLine, TerminalAccessibleBufferProvider } from './terminalAccessibleBufferProvider.js'; import { TextAreaSyncAddon } from './textAreaSyncAddon.js'; +import { alert } from '../../../../../base/browser/ui/aria/aria.js'; // #region Terminal Contributions From 8be4be068e75248770be81abb5e8fc9f5d2e2302 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 19 Dec 2024 19:21:00 +0100 Subject: [PATCH 408/479] Fixes bug (#236620) --- .../browser/view/inlineEdits/gutterIndicatorView.ts | 2 +- 1 file changed, 1 insertion(+), 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 b0f8b3dc54d..739f12b4feb 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -108,8 +108,8 @@ export class InlineEditsGutterIndicator extends Disposable { private readonly _tabAction = derived(this, reader => { const m = this._model.read(reader); - if (m && m.tabShouldAcceptInlineEdit.read(reader)) { return 'accept' as const; } if (m && m.tabShouldJumpToInlineEdit.read(reader)) { return 'jump' as const; } + if (m && m.tabShouldAcceptInlineEdit.read(reader)) { return 'accept' as const; } return 'inactive' as const; }); From d55cb9a7a04ab9b894b3d3f51ae5a0f4589bf924 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 19 Dec 2024 10:25:37 -0800 Subject: [PATCH 409/479] Use claims to force an idToken in Broker flow (#236623) Looks like the Broker doesn't support `forceRefresh`... This is an alternative way of forcing a refresh. Fixes https://github.com/microsoft/vscode/issues/229456 --- .../src/node/cachedPublicClientApplication.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts index 7396da17990..0f27c2c0e4d 100644 --- a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts +++ b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts @@ -102,9 +102,19 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica ); if (fiveMinutesBefore < new Date()) { this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is expired or about to expire. Forcing refresh...`); - result = await this._sequencer.queue(() => this._pca.acquireTokenSilent({ ...request, forceRefresh: true })); + const newRequest = this._isBrokerAvailable + // HACK: Broker doesn't support forceRefresh so we need to pass in claims which will force a refresh + ? { ...request, claims: '{ "id_token": {}}' } + : { ...request, forceRefresh: true }; + result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(newRequest)); this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got refreshed result`); } + const newIdTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp; + if (newIdTokenExpirationInSecs) { + if (new Date(newIdTokenExpirationInSecs * 1000) < new Date()) { + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); + } + } } // this._setupRefresh(result); From fe68aa5447827cbdc970fa14e572ab8c0b572f71 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 19 Dec 2024 12:52:06 -0600 Subject: [PATCH 410/479] fix go to symbol in accessible view (#236624) --- .../accessibility/browser/accessibleView.ts | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index c9acf7b6a28..fde082ed5d5 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -78,7 +78,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi private _hasAssignedKeybindings: IContextKey; private _codeBlocks?: ICodeBlock[]; - private _inQuickPick: boolean = false; + private _isInQuickPick: boolean = false; get editorWidget() { return this._editorWidget; } private _container: HTMLElement; @@ -352,6 +352,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi if (!this._currentProvider) { return; } + this._isInQuickPick = true; this._instantiationService.createInstance(AccessibleViewSymbolQuickPick, this).show(this._currentProvider); } @@ -388,11 +389,11 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi } getSymbols(): IAccessibleViewSymbol[] | undefined { - const provider = this._currentProvider instanceof AccessibleContentProvider ? this._currentProvider : undefined; + const provider = this._currentProvider ? this._currentProvider : undefined; if (!this._currentContent || !provider) { return; } - const symbols: IAccessibleViewSymbol[] = provider.getSymbols?.() || []; + const symbols: IAccessibleViewSymbol[] = 'getSymbols' in provider ? provider.getSymbols?.() || [] : []; if (symbols?.length) { return symbols; } @@ -416,7 +417,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi } configureKeybindings(unassigned: boolean): void { - this._inQuickPick = true; + this._isInQuickPick = true; const provider = this._updateLastProvider(); const items = unassigned ? provider?.options?.configureKeybindingItems : provider?.options?.configuredKeybindingItems; if (!items) { @@ -440,7 +441,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi this.show(provider); } disposables.dispose(); - this._inQuickPick = false; + this._isInQuickPick = false; })); } @@ -495,6 +496,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi if (lineNumber === undefined) { return; } + this._isInQuickPick = false; this.show(provider, undefined, undefined, { lineNumber, column: 1 }); this._updateContextKeys(provider, true); } @@ -609,11 +611,14 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi this._updateToolbar(this._currentProvider.actions, provider.options.type); const hide = (e?: KeyboardEvent | IKeyboardEvent): void => { - if (!this._inQuickPick) { + if (!this._isInQuickPick) { provider.onClose(); } e?.stopPropagation(); this._contextViewService.hideContextView(); + if (this._isInQuickPick) { + return; + } this._updateContextKeys(provider, false); this._lastProvider = undefined; this._currentContent = undefined; @@ -938,11 +943,15 @@ class AccessibleViewSymbolQuickPick { for (const symbol of symbols) { picks.push({ label: symbol.label, - ariaLabel: symbol.ariaLabel + ariaLabel: symbol.ariaLabel, + firstListItem: symbol.firstListItem, + lineNumber: symbol.lineNumber, + endLineNumber: symbol.endLineNumber, + markdownToParse: symbol.markdownToParse }); } quickPick.canSelectMany = false; - quickPick.items = symbols; + quickPick.items = picks; quickPick.show(); disposables.add(quickPick.onDidAccept(() => { this._accessibleView.showSymbol(provider, quickPick.selectedItems[0]); From d34e04970c4ad3955693708dd2c481bf7c8ccd80 Mon Sep 17 00:00:00 2001 From: RedCMD <33529441+RedCMD@users.noreply.github.com> Date: Fri, 20 Dec 2024 07:54:50 +1300 Subject: [PATCH 411/479] Add `outdated` and `recentlyUpdated` suggestions to extension filter (#235884) --- src/vs/workbench/contrib/extensions/common/extensionQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts index d6cd57bb5bc..68320b1455a 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts @@ -12,7 +12,7 @@ export class Query { } static suggestions(query: string): string[] { - const commands = ['installed', 'updates', 'enabled', 'disabled', 'builtin', 'featured', 'popular', 'recommended', 'recentlyPublished', 'workspaceUnsupported', 'deprecated', 'sort', 'category', 'tag', 'ext', 'id'] as const; + const commands = ['installed', 'updates', 'enabled', 'disabled', 'builtin', 'featured', 'popular', 'recommended', 'recentlyPublished', 'workspaceUnsupported', 'deprecated', 'sort', 'category', 'tag', 'ext', 'id', 'outdated', 'recentlyUpdated'] as const; const subcommands = { 'sort': ['installs', 'rating', 'name', 'publishedDate', 'updateDate'], 'category': EXTENSION_CATEGORIES.map(c => `"${c.toLowerCase()}"`), From 1cbc2eedcd75198db22137ab81701d09d6ee1290 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 19 Dec 2024 13:30:17 -0600 Subject: [PATCH 412/479] rm `auto` as a type for voice synthesizer setting (#236616) fix #229403 --- .../accessibility/browser/accessibilityConfiguration.ts | 5 ++--- .../chat/electron-sandbox/actions/voiceChatActions.ts | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 32eff1e1594..fc060c2832e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -806,14 +806,13 @@ export class DynamicSpeechAccessibilityConfiguration extends Disposable implemen }, [AccessibilityVoiceSettingId.AutoSynthesize]: { 'type': 'string', - 'enum': ['on', 'off', 'auto'], + 'enum': ['on', 'off'], 'enumDescriptions': [ localize('accessibility.voice.autoSynthesize.on', "Enable the feature. When a screen reader is enabled, note that this will disable aria updates."), localize('accessibility.voice.autoSynthesize.off', "Disable the feature."), - localize('accessibility.voice.autoSynthesize.auto', "When a screen reader is detected, disable the feature. Otherwise, enable the feature.") ], 'markdownDescription': localize('autoSynthesize', "Whether a textual response should automatically be read out aloud when speech was used as input. For example in a chat session, a response is automatically synthesized when voice was used as chat request."), - 'default': this.productService.quality !== 'stable' ? 'auto' : 'off', + 'default': this.productService.quality !== 'stable' ? 'on' : 'off', 'tags': ['accessibility'] } } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 57310027bc9..f3fe9985e72 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -369,8 +369,8 @@ class VoiceChatSessions { if (!response) { return; } - const autoSynthesize = this.configurationService.getValue<'on' | 'off' | 'auto'>(AccessibilityVoiceSettingId.AutoSynthesize); - if (autoSynthesize === 'on' || autoSynthesize === 'auto' && !this.accessibilityService.isScreenReaderOptimized()) { + const autoSynthesize = this.configurationService.getValue<'on' | 'off'>(AccessibilityVoiceSettingId.AutoSynthesize); + if (autoSynthesize === 'on' || (autoSynthesize !== 'off' && !this.accessibilityService.isScreenReaderOptimized())) { let context: IVoiceChatSessionController | 'focused'; if (controller.context === 'inline') { // This is ugly, but the lightweight inline chat turns into From 2a99d4331539247b4933b992ec768653d3634b61 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Thu, 19 Dec 2024 11:44:22 -0800 Subject: [PATCH 413/479] fix: don't watch untitled files for chat editing (#236634) --- .../chatEditing/chatEditingModifiedFileEntry.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index 6becb086793..aa105b2e767 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts @@ -6,6 +6,7 @@ import { RunOnceScheduler } from '../../../../../base/common/async.js'; import { Emitter } from '../../../../../base/common/event.js'; import { Disposable, IReference, toDisposable } from '../../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../../base/common/network.js'; import { IObservable, ITransaction, observableValue, transaction } from '../../../../../base/common/observable.js'; import { themeColorFromId } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; @@ -171,12 +172,15 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie this._register(this.doc.onDidChangeContent(e => this._mirrorEdits(e))); - this._register(this._fileService.watch(this.modifiedURI)); - this._register(this._fileService.onDidFilesChange(e => { - if (e.affects(this.modifiedURI) && kind === ChatEditKind.Created && e.gotDeleted()) { - this._onDidDelete.fire(); - } - })); + + if (this.modifiedURI.scheme !== Schemas.untitled) { + this._register(this._fileService.watch(this.modifiedURI)); + this._register(this._fileService.onDidFilesChange(e => { + if (e.affects(this.modifiedURI) && kind === ChatEditKind.Created && e.gotDeleted()) { + this._onDidDelete.fire(); + } + })); + } this._register(toDisposable(() => { this._clearCurrentEditLineDecoration(); From 2217f51cf8d7022cafe49301a523ea55cbddc5b4 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Thu, 19 Dec 2024 21:01:41 +0100 Subject: [PATCH 414/479] Fix title bar focus command when hidden (#236635) fix #236597 --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 3facaf51e23..32dad57a218 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -54,6 +54,7 @@ import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionba import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { safeIntl } from '../../../../base/common/date.js'; +import { TitleBarVisibleContext } from '../../../common/contextkeys.js'; export interface ITitleVariable { readonly name: string; @@ -119,11 +120,12 @@ export class BrowserTitleService extends MultiWindowParts i title: localize2('focusTitleBar', 'Focus Title Bar'), category: Categories.View, f1: true, + precondition: TitleBarVisibleContext }); } run(): void { - that.getPartByDocument(getActiveDocument()).focus(); + that.getPartByDocument(getActiveDocument())?.focus(); } })); } From e48d729d217084342cacf71bbfa6c1fa0d867feb Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 19 Dec 2024 12:09:55 -0800 Subject: [PATCH 415/479] Apply margin to action items (#236638) Fixes https://github.com/microsoft/vscode/issues/233699 --- .../platform/quickinput/browser/media/quickInput.css | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/quickinput/browser/media/quickInput.css b/src/vs/platform/quickinput/browser/media/quickInput.css index 9108ee9ae80..53f42dfa5ae 100644 --- a/src/vs/platform/quickinput/browser/media/quickInput.css +++ b/src/vs/platform/quickinput/browser/media/quickInput.css @@ -26,8 +26,14 @@ flex: 1; } -.quick-input-inline-action-bar { - margin: 2px 0 0 5px; +/* give some space between input and action bar */ +.quick-input-inline-action-bar > .actions-container > .action-item:first-child { + margin-left: 5px; +} + +/* center horizontally */ +.quick-input-inline-action-bar > .actions-container > .action-item { + margin-top: 2px; } .quick-input-title { From 358e96ab1e9017d8dc90b7a24e617d9f39e017b4 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 19 Dec 2024 12:40:19 -0800 Subject: [PATCH 416/479] Cancel if the user dismisses the modal (#236642) Fixes https://github.com/microsoft/vscode/issues/235364 --- .../microsoft-authentication/src/node/authProvider.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/microsoft-authentication/src/node/authProvider.ts b/extensions/microsoft-authentication/src/node/authProvider.ts index af34273afa4..cc8eb2bc5c7 100644 --- a/extensions/microsoft-authentication/src/node/authProvider.ts +++ b/extensions/microsoft-authentication/src/node/authProvider.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 { AccountInfo, AuthenticationResult, ServerError } from '@azure/msal-node'; +import { AccountInfo, AuthenticationResult, ClientAuthError, ClientAuthErrorCodes, ServerError } from '@azure/msal-node'; import { AuthenticationGetSessionOptions, AuthenticationProvider, AuthenticationProviderAuthenticationSessionsChangeEvent, AuthenticationProviderSessionOptions, AuthenticationSession, AuthenticationSessionAccountInformation, CancellationError, env, EventEmitter, ExtensionContext, l10n, LogOutputChannel, Uri, window } from 'vscode'; import { Environment } from '@azure/ms-rest-azure-env'; import { CachedPublicClientApplicationManager } from './publicClientCache'; @@ -229,6 +229,12 @@ export class MsalAuthProvider implements AuthenticationProvider { throw e; } + // The user closed the modal window + if ((e as ClientAuthError).errorCode === ClientAuthErrorCodes.userCanceled) { + this._telemetryReporter.sendLoginFailedEvent(); + throw e; + } + // The user wants to try the loopback client or we got an error likely due to spinning up the server const loopbackClient = new UriHandlerLoopbackClient(this._uriHandler, redirectUri, this._logger); try { From ad359b1ca9090f340655e8839a76abb9b59f8761 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Thu, 19 Dec 2024 12:58:38 -0800 Subject: [PATCH 417/479] Clear nb selection highlights on non-explicit cursor events (#236641) ugh I overthought this. clear on non-explicit cursor events. (ie typing) --- .../browser/contrib/multicursor/notebookSelectionHighlight.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts index a7e82c41a73..305e4d8fde4 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts @@ -63,6 +63,7 @@ class NotebookSelectionHighlighter extends Disposable implements INotebookEditor this.anchorDisposables.clear(); this.anchorDisposables.add(this.anchorCell[1].onDidChangeCursorPosition((e) => { if (e.reason !== CursorChangeReason.Explicit) { + this.clearNotebookSelectionDecorations(); return; } From 6ec5e7b296e7d96e8fbd87d1329bf5642d738da2 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 19 Dec 2024 13:06:18 -0800 Subject: [PATCH 418/479] debug: fix tree 'measuring node not in DOM error' (#236645) --- .../workbench/contrib/debug/browser/repl.ts | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index edef7e72b70..04823d1f137 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -174,7 +174,11 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { this.onDidFocusSession(this.debugService.getViewModel().focusedSession); } - this._register(this.debugService.getViewModel().onDidFocusSession(async session => this.onDidFocusSession(session))); + this._register(this.debugService.getViewModel().onDidFocusSession(session => { + if (this.isVisible()) { + this.onDidFocusSession(session); + } + })); this._register(this.debugService.getViewModel().onDidEvaluateLazyExpression(async e => { if (e instanceof Variable && this.tree?.hasNode(e)) { await this.tree.updateChildren(e, false, true); @@ -201,19 +205,27 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { } })); this._register(this.onDidChangeBodyVisibility(visible => { - if (visible) { - if (!this.model) { - this.model = this.modelService.getModel(Repl.URI) || this.modelService.createModel('', null, Repl.URI, true); - } - this.setMode(); - this.replInput.setModel(this.model); - this.updateInputDecoration(); - this.refreshReplElements(true); - if (this.styleChangedWhenInvisible) { - this.styleChangedWhenInvisible = false; - this.tree?.updateChildren(undefined, true, false); - this.onDidStyleChange(); - } + if (!visible) { + return; + } + if (!this.model) { + this.model = this.modelService.getModel(Repl.URI) || this.modelService.createModel('', null, Repl.URI, true); + } + + const focusedSession = this.debugService.getViewModel().focusedSession; + if (this.tree && this.tree.getInput() !== focusedSession) { + this.onDidFocusSession(focusedSession); + } + + this.setMode(); + this.replInput.setModel(this.model); + this.updateInputDecoration(); + this.refreshReplElements(true); + + if (this.styleChangedWhenInvisible) { + this.styleChangedWhenInvisible = false; + this.tree?.updateChildren(undefined, true, false); + this.onDidStyleChange(); } })); this._register(this.configurationService.onDidChangeConfiguration(e => { From f442df17470c5b95936acc79bbca27509177cf31 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 19 Dec 2024 22:06:52 +0100 Subject: [PATCH 419/479] Git - better match git conflict decorations (#236646) --- extensions/git/src/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index dde1c99049a..95ab9ada071 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -309,7 +309,7 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ const isBothAddedOrModified = (s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED; const isAnyDeleted = (s: Resource) => s.type === Status.DELETED_BY_THEM || s.type === Status.DELETED_BY_US; const possibleUnresolved = merge.filter(isBothAddedOrModified); - const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/)); + const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}\s|^={7}$|^>{7}\s/)); const unresolvedBothModified = await Promise.all(promises); const resolved = possibleUnresolved.filter((_s, i) => !unresolvedBothModified[i]); const deletionConflicts = merge.filter(s => isAnyDeleted(s)); From ee8c3e9e8acd54968405d96dea233b31c32c6830 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 19 Dec 2024 15:52:14 -0600 Subject: [PATCH 420/479] add `Terminal: resize suggest widget size` command (#236639) fix #235091 --- .../suggest/browser/terminal.suggest.contribution.ts | 6 ++++++ .../terminalContrib/suggest/browser/terminalSuggestAddon.ts | 4 ++++ .../terminalContrib/suggest/common/terminal.suggest.ts | 1 + .../services/suggest/browser/simpleSuggestWidget.ts | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts index 96676a5a12c..35a845c1f7b 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts @@ -195,6 +195,12 @@ registerActiveInstanceAction({ run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.requestCompletions(true) }); +registerActiveInstanceAction({ + id: TerminalSuggestCommandId.ResetWidgetSize, + title: localize2('workbench.action.terminal.resetSuggestWidgetSize', 'Reset Suggest Widget Size'), + run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.resetWidgetSize() +}); + registerActiveInstanceAction({ id: TerminalSuggestCommandId.SelectPrevSuggestion, title: localize2('workbench.action.terminal.selectPrevSuggestion', 'Select the Previous Suggestion'), diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index 9f173cfc15a..94650c7525c 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -231,6 +231,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._screen = screen; } + resetWidgetSize(): void { + this._suggestWidget?.resetWidgetSize(); + } + async requestCompletions(explicitlyInvoked?: boolean): Promise { if (!this._promptInputModel) { return; diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts index b9fe0dcbe60..0b62f790124 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts @@ -13,6 +13,7 @@ export const enum TerminalSuggestCommandId { HideSuggestWidget = 'workbench.action.terminal.hideSuggestWidget', ClearSuggestCache = 'workbench.action.terminal.clearSuggestCache', RequestCompletions = 'workbench.action.terminal.requestCompletions', + ResetWidgetSize = 'workbench.action.terminal.resetSuggestWidgetSize', } export const defaultTerminalSuggestCommandsToSkipShell = [ diff --git a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts index bc9c9ea0cba..af70d32b896 100644 --- a/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts +++ b/src/vs/workbench/services/suggest/browser/simpleSuggestWidget.ts @@ -290,6 +290,10 @@ export class SimpleSuggestWidget extends Disposable { return this._completionModel?.items.length !== 0; } + resetWidgetSize(): void { + this._persistedSize.reset(); + } + showSuggestions(selectionIndex: number, isFrozen: boolean, isAuto: boolean, cursorPosition: { top: number; left: number; height: number }): void { this._cursorPosition = cursorPosition; From 41793c2d43f18397608003a7af524ce660b0b4c3 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 19 Dec 2024 16:26:26 -0600 Subject: [PATCH 421/479] use keyboard vs key icon for configure keybinding action icons (#236649) fix #229827 --- .../contrib/accessibility/browser/accessibleViewActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index d1be576a131..f587bd7cb1e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -233,7 +233,7 @@ class AccessibilityHelpConfigureKeybindingsAction extends Action2 { super({ id: AccessibilityCommandId.AccessibilityHelpConfigureKeybindings, precondition: ContextKeyExpr.and(accessibilityHelpIsShown, accessibleViewHasUnassignedKeybindings), - icon: Codicon.key, + icon: Codicon.recordKeys, keybinding: { primary: KeyMod.Alt | KeyCode.KeyK, weight: KeybindingWeight.WorkbenchContrib @@ -260,7 +260,7 @@ class AccessibilityHelpConfigureAssignedKeybindingsAction extends Action2 { super({ id: AccessibilityCommandId.AccessibilityHelpConfigureAssignedKeybindings, precondition: ContextKeyExpr.and(accessibilityHelpIsShown, accessibleViewHasAssignedKeybindings), - icon: Codicon.key, + icon: Codicon.recordKeys, keybinding: { primary: KeyMod.Alt | KeyCode.KeyA, weight: KeybindingWeight.WorkbenchContrib From 38c3ebef2e3e9069552b835855271dcf0ac3fe4e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 Dec 2024 17:39:08 -0800 Subject: [PATCH 422/479] Remove repeated call, warn on invalid persisted data (#236658) --- src/vs/workbench/contrib/chat/common/chatModel.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 14fa03dffa0..d1b092b114c 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -999,12 +999,17 @@ export class ChatModel extends Disposable implements IChatModel { ) { super(); - this._isImported = (!!initialData && !isSerializableSessionData(initialData)) || (initialData?.isImported ?? false); - this._sessionId = (isSerializableSessionData(initialData) && initialData.sessionId) || generateUuid(); + const isValid = isSerializableSessionData(initialData); + if (initialData && !isValid) { + this.logService.warn(`ChatModel#constructor: Loaded malformed session data: ${JSON.stringify(initialData)}`); + } + + this._isImported = (!!initialData && !isValid) || (initialData?.isImported ?? false); + this._sessionId = (isValid && initialData.sessionId) || generateUuid(); this._requests = initialData ? this._deserialize(initialData) : []; - this._creationDate = (isSerializableSessionData(initialData) && initialData.creationDate) || Date.now(); - this._lastMessageDate = (isSerializableSessionData(initialData) && initialData.lastMessageDate) || this._creationDate; - this._customTitle = isSerializableSessionData(initialData) ? initialData.customTitle : undefined; + this._creationDate = (isValid && initialData.creationDate) || Date.now(); + this._lastMessageDate = (isValid && initialData.lastMessageDate) || this._creationDate; + this._customTitle = isValid ? initialData.customTitle : undefined; this._initialRequesterAvatarIconUri = initialData?.requesterAvatarIconUri && URI.revive(initialData.requesterAvatarIconUri); this._initialResponderAvatarIconUri = isUriComponents(initialData?.responderAvatarIconUri) ? URI.revive(initialData.responderAvatarIconUri) : initialData?.responderAvatarIconUri; From 89f808979a5151bd91324e65d4f7ab1b62896983 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 19 Dec 2024 19:39:33 -0800 Subject: [PATCH 423/479] Cancel request tool calls when cancelling chat request (#236663) * Cancel request tool calls when cancelling chat request Fix #232775 * Don't invoke tool if cancelled before * Fix tests --- .../chat/browser/languageModelToolsService.ts | 72 +++++++++++++++++-- .../contrib/chat/common/chatServiceImpl.ts | 11 ++- .../chat/common/languageModelToolsService.ts | 1 + .../browser/languageModelToolsService.test.ts | 71 +++++++++++++++++- .../chat/test/common/mockChatService.ts | 7 +- .../common/mockLanguageModelToolsService.ts | 3 + .../test/browser/inlineChatController.test.ts | 3 + 7 files changed, 156 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index 2b3f6b2dc49..d0bce841451 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -5,11 +5,11 @@ import { renderStringAsPlaintext } from '../../../../base/browser/markdownRenderer.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; -import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { CancellationError, isCancellationError } from '../../../../base/common/errors.js'; import { Emitter } from '../../../../base/common/event.js'; import { Iterable } from '../../../../base/common/iterator.js'; -import { Disposable, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { localize } from '../../../../nls.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; @@ -37,6 +37,9 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo private _tools = new Map(); private _toolContextKeys = new Set(); + + private _callsByRequestId = new Map(); + constructor( @IExtensionService private readonly _extensionService: IExtensionService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -141,10 +144,34 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo // Shortcut to write to the model directly here, but could call all the way back to use the real stream. let toolInvocation: ChatToolInvocation | undefined; + let requestId: string | undefined; + let store: DisposableStore | undefined; try { if (dto.context) { - const model = this._chatService.getSession(dto.context?.sessionId) as ChatModel; + store = new DisposableStore(); + const model = this._chatService.getSession(dto.context?.sessionId) as ChatModel | undefined; + if (!model) { + throw new Error(`Tool called for unknown chat session`); + } + const request = model.getRequests().at(-1)!; + requestId = request.id; + + // Replace the token with a new token that we can cancel when cancelToolCallsForRequest is called + if (!this._callsByRequestId.has(requestId)) { + this._callsByRequestId.set(requestId, []); + } + this._callsByRequestId.get(requestId)!.push(store); + + const source = new CancellationTokenSource(); + store.add(toDisposable(() => { + toolInvocation!.confirmed.complete(false); + source.dispose(true); + })); + store.add(token.onCancellationRequested(() => { + source.cancel(); + })); + token = source.token; const prepared = tool.impl.prepareToolInvocation ? await tool.impl.prepareToolInvocation(dto.parameters, token) @@ -153,9 +180,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo const defaultMessage = localize('toolInvocationMessage', "Using {0}", `"${tool.data.displayName}"`); const invocationMessage = prepared?.invocationMessage ?? defaultMessage; toolInvocation = new ChatToolInvocation(invocationMessage, prepared?.confirmationMessages); - token.onCancellationRequested(() => { - toolInvocation!.confirmed.complete(false); - }); + model.acceptResponseProgress(request, toolInvocation); if (prepared?.confirmationMessages) { const userConfirmed = await toolInvocation.confirmed.p; @@ -176,6 +201,9 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo } } + if (token.isCancellationRequested) { + throw new CancellationError(); + } const result = await tool.impl.invoke(dto, countTokens, token); this._telemetryService.publicLog2( @@ -200,7 +228,39 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo throw err; } finally { toolInvocation?.isCompleteDeferred.complete(); + + if (requestId && store) { + this.cleanupCallDisposables(requestId, store); + } + } + } + + private cleanupCallDisposables(requestId: string, store: DisposableStore): void { + const disposables = this._callsByRequestId.get(requestId); + if (disposables) { + const index = disposables.indexOf(store); + if (index > -1) { + disposables.splice(index, 1); + } + if (disposables.length === 0) { + this._callsByRequestId.delete(requestId); + } } + store.dispose(); + } + + cancelToolCallsForRequest(requestId: string): void { + const calls = this._callsByRequestId.get(requestId); + if (calls) { + calls.forEach(call => call.dispose()); + this._callsByRequestId.delete(requestId); + } + } + + public override dispose(): void { + super.dispose(); + + this._callsByRequestId.forEach(calls => dispose(calls)); } } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index d5a8839dd11..6cfb29ce0c9 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -33,6 +33,7 @@ import { ChatServiceTelemetry } from './chatServiceTelemetry.js'; import { IChatSlashCommandService } from './chatSlashCommands.js'; import { IChatVariablesService } from './chatVariables.js'; import { ChatMessageRole, IChatMessage } from './languageModels.js'; +import { ILanguageModelToolsService } from './languageModelToolsService.js'; const serializedChatKey = 'interactive.sessions'; @@ -86,7 +87,8 @@ const maxPersistedSessions = 25; class CancellableRequest implements IDisposable { constructor( public readonly cancellationTokenSource: CancellationTokenSource, - public requestId?: string | undefined + public requestId: string | undefined, + @ILanguageModelToolsService private readonly toolsService: ILanguageModelToolsService ) { } dispose() { @@ -94,6 +96,10 @@ class CancellableRequest implements IDisposable { } cancel() { + if (this.requestId) { + this.toolsService.cancelToolCallsForRequest(this.requestId); + } + this.cancellationTokenSource.cancel(); } } @@ -778,7 +784,8 @@ export class ChatService extends Disposable implements IChatService { } }; const rawResponsePromise = sendRequestInternal(); - this._pendingRequests.set(model.sessionId, new CancellableRequest(source)); + // Note- requestId is not known at this point, assigned later + this._pendingRequests.set(model.sessionId, this.instantiationService.createInstance(CancellableRequest, source, undefined)); rawResponsePromise.finally(() => { this._pendingRequests.deleteAndDispose(model.sessionId); }); diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index f2251ca8166..f69be4fed5c 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -86,4 +86,5 @@ export interface ILanguageModelToolsService { getTool(id: string): IToolData | undefined; getToolByName(name: string): IToolData | undefined; invokeTool(invocation: IToolInvocation, countTokens: CountTokensCallback, token: CancellationToken): Promise; + cancelToolCallsForRequest(requestId: string): void; } diff --git a/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts b/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts index 9679c7a8908..fb866bbfade 100644 --- a/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts @@ -6,24 +6,32 @@ import * as assert from 'assert'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; +import { ContextKeyService } from '../../../../../platform/contextkey/browser/contextKeyService.js'; import { ContextKeyEqualsExpr, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js'; import { LanguageModelToolsService } from '../../browser/languageModelToolsService.js'; +import { IChatModel } from '../../common/chatModel.js'; +import { IChatService } from '../../common/chatService.js'; import { IToolData, IToolImpl, IToolInvocation } from '../../common/languageModelToolsService.js'; -import { ContextKeyService } from '../../../../../platform/contextkey/browser/contextKeyService.js'; -import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; +import { MockChatService } from '../common/mockChatService.js'; +import { CancellationError, isCancellationError } from '../../../../../base/common/errors.js'; +import { Barrier } from '../../../../../base/common/async.js'; suite('LanguageModelToolsService', () => { const store = ensureNoDisposablesAreLeakedInTestSuite(); let contextKeyService: IContextKeyService; let service: LanguageModelToolsService; + let chatService: MockChatService; setup(() => { const instaService = workbenchInstantiationService({ - contextKeyService: () => store.add(new ContextKeyService(new TestConfigurationService)) + contextKeyService: () => store.add(new ContextKeyService(new TestConfigurationService)), }, store); contextKeyService = instaService.get(IContextKeyService); + chatService = new MockChatService(); + instaService.stub(IChatService, chatService); service = store.add(instaService.createInstance(LanguageModelToolsService)); }); @@ -122,4 +130,61 @@ suite('LanguageModelToolsService', () => { const result = await service.invokeTool(dto, async () => 0, CancellationToken.None); assert.strictEqual(result.content[0].value, 'result'); }); + + test('cancel tool call', async () => { + const toolData: IToolData = { + id: 'testTool', + modelDescription: 'Test Tool', + displayName: 'Test Tool' + }; + + store.add(service.registerToolData(toolData)); + + const toolBarrier = new Barrier(); + const toolImpl: IToolImpl = { + invoke: async (invocation, countTokens, cancelToken) => { + assert.strictEqual(invocation.callId, '1'); + assert.strictEqual(invocation.toolId, 'testTool'); + assert.deepStrictEqual(invocation.parameters, { a: 1 }); + await toolBarrier.wait(); + if (cancelToken.isCancellationRequested) { + throw new CancellationError(); + } else { + throw new Error('Tool call should be cancelled'); + } + } + }; + + store.add(service.registerToolImplementation('testTool', toolImpl)); + + const sessionId = 'sessionId'; + const requestId = 'requestId'; + const dto: IToolInvocation = { + callId: '1', + toolId: 'testTool', + tokenBudget: 100, + parameters: { + a: 1 + }, + context: { + sessionId + }, + }; + chatService.addSession({ + sessionId: sessionId, + getRequests: () => { + return [{ + id: requestId + }]; + }, + acceptResponseProgress: () => { } + } as any as IChatModel); + + const toolPromise = service.invokeTool(dto, async () => 0, CancellationToken.None); + service.cancelToolCallsForRequest(requestId); + toolBarrier.open(); + await assert.rejects(toolPromise, err => { + return isCancellationError(err); + }, 'Expected tool call to be cancelled'); + }); }); diff --git a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts index ad4262a7dbd..31fbb654b63 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts @@ -15,6 +15,8 @@ export class MockChatService implements IChatService { _serviceBrand: undefined; transferredSessionData: IChatTransferredSessionData | undefined; + private sessions = new Map(); + isEnabled(location: ChatAgentLocation): boolean { throw new Error('Method not implemented.'); } @@ -27,9 +29,12 @@ export class MockChatService implements IChatService { startSession(location: ChatAgentLocation, token: CancellationToken): ChatModel | undefined { throw new Error('Method not implemented.'); } + addSession(session: IChatModel): void { + this.sessions.set(session.sessionId, session); + } getSession(sessionId: string): IChatModel | undefined { // eslint-disable-next-line local/code-no-dangerous-type-assertions - return {} as IChatModel; + return this.sessions.get(sessionId) ?? {} as IChatModel; } getOrRestoreSession(sessionId: string): IChatModel | undefined { throw new Error('Method not implemented.'); diff --git a/src/vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService.ts b/src/vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService.ts index a192334cb9d..d854a0beb5e 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockLanguageModelToolsService.ts @@ -13,6 +13,9 @@ export class MockLanguageModelToolsService implements ILanguageModelToolsService constructor() { } + cancelToolCallsForRequest(requestId: string): void { + } + onDidChangeTools: Event = Event.None; registerToolData(toolData: IToolData): IDisposable { diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 7d07547770e..75db4dbb7f1 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -71,6 +71,8 @@ import { ITextModelService } from '../../../../../editor/common/services/resolve import { TextModelResolverService } from '../../../../services/textmodelResolver/common/textModelResolverService.js'; import { ChatInputBoxContentProvider } from '../../../chat/browser/chatEdinputInputContentProvider.js'; import { IObservable, observableValue } from '../../../../../base/common/observable.js'; +import { ILanguageModelToolsService } from '../../../chat/common/languageModelToolsService.js'; +import { MockLanguageModelToolsService } from '../../../chat/test/common/mockLanguageModelToolsService.js'; suite('InlineChatController', function () { @@ -198,6 +200,7 @@ suite('InlineChatController', function () { [IWorkbenchAssignmentService, new NullWorkbenchAssignmentService()], [ILanguageModelsService, new SyncDescriptor(LanguageModelsService)], [ITextModelService, new SyncDescriptor(TextModelResolverService)], + [ILanguageModelToolsService, new SyncDescriptor(MockLanguageModelToolsService)], ); instaService = store.add((store.add(workbenchInstantiationService(undefined, store))).createChild(serviceCollection)); From 77cec55e495ea7b9dceee6f589c781799e325d20 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:53:35 +0100 Subject: [PATCH 424/479] Git - git installation welcome view should use remoteName context key (#236672) --- extensions/git/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index d63027619d1..53aa0fad747 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3372,22 +3372,22 @@ { "view": "scm", "contents": "%view.workbench.scm.missing%", - "when": "config.git.enabled && git.missing" + "when": "config.git.enabled && git.missing && remoteName != ''" }, { "view": "scm", "contents": "%view.workbench.scm.missing.mac%", - "when": "config.git.enabled && git.missing && isMac" + "when": "config.git.enabled && git.missing && remoteName == '' && isMac" }, { "view": "scm", "contents": "%view.workbench.scm.missing.windows%", - "when": "config.git.enabled && git.missing && isWindows" + "when": "config.git.enabled && git.missing && remoteName == '' && isWindows" }, { "view": "scm", "contents": "%view.workbench.scm.missing.linux%", - "when": "config.git.enabled && git.missing && isLinux" + "when": "config.git.enabled && git.missing && remoteName == '' && isLinux" }, { "view": "scm", From da59ef74e2587e707944db56bcc0f84a9e6e3ff8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 20 Dec 2024 09:11:12 +0100 Subject: [PATCH 425/479] workbench.action.focus*GroupWithoutWrap commands fail to focus *-most group when sibebar/panel is focused (fix #236648) (#236673) --- src/vs/workbench/browser/parts/editor/editorCommands.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 1bbe6258eb0..20c084c9603 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -312,6 +312,7 @@ function registerActiveEditorMoveCopyCommand(): void { } else if (sourceGroup.id !== targetGroup.id) { sourceGroup.copyEditors(editors.map(editor => ({ editor })), targetGroup); } + targetGroup.focus(); } } @@ -972,8 +973,8 @@ function registerFocusEditorGroupWihoutWrapCommands(): void { CommandsRegistry.registerCommand(command.id, async (accessor: ServicesAccessor) => { const editorGroupsService = accessor.get(IEditorGroupsService); - const group = editorGroupsService.findGroup({ direction: command.direction }, editorGroupsService.activeGroup, false); - group?.focus(); + const group = editorGroupsService.findGroup({ direction: command.direction }, editorGroupsService.activeGroup, false) ?? editorGroupsService.activeGroup; + group.focus(); }); } } From b9084edd1cf2414e21de4f9e5b5acae240a22218 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:08:22 +0100 Subject: [PATCH 426/479] Git - file system provider should throw `FileNotFound` if the resource does not exist in git instead of returning an empty file (#236676) --- extensions/git/src/fileSystemProvider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index af80924ae13..24ae4e6df9a 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -192,7 +192,8 @@ export class GitFileSystemProvider implements FileSystemProvider { try { return await repository.buffer(sanitizeRef(ref, path, repository), path); } catch (err) { - return new Uint8Array(0); + // File does not exist in git (ex: git ignored) + throw FileSystemError.FileNotFound(); } } From 91ced52bc8547ce1c21138ee4a6a5061cf995c92 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 20 Dec 2024 11:01:48 +0100 Subject: [PATCH 427/479] Add the possibility to define context keys on CodeEditorWidgets --- .../browser/widget/codeEditor/codeEditorWidget.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts index ed20f95580a..3bd2f0a903e 100644 --- a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts @@ -290,6 +290,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE })); this._contextKeyService = this._register(contextKeyService.createScoped(this._domElement)); + if (codeEditorWidgetOptions.contextKeyValues) { + for (const [key, value] of Object.entries(codeEditorWidgetOptions.contextKeyValues)) { + this._contextKeyService.createKey(key, value); + } + } this._notificationService = notificationService; this._codeEditorService = codeEditorService; this._commandService = commandService; @@ -1988,6 +1993,12 @@ export interface ICodeEditorWidgetOptions { * Defaults to MenuId.SimpleEditorContext or MenuId.EditorContext depending on whether the widget is simple. */ contextMenuId?: MenuId; + + /** + * Define extra context keys that will be defined in the context service + * for the editor. + */ + contextKeyValues?: Record; } class ModelData { From 7d4b23f21adb12d47f582388ce96f9e447908f77 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 20 Dec 2024 11:02:35 +0100 Subject: [PATCH 428/479] Move `getOuterEditor` near the EmbeddedCodeEditorWidget --- .../widget/codeEditor/embeddedCodeEditorWidget.ts | 10 +++++++++- .../gotoSymbol/browser/peek/referencesController.ts | 3 ++- src/vs/editor/contrib/peekView/browser/peekView.ts | 11 +---------- .../workbench/contrib/scm/browser/quickDiffWidget.ts | 3 ++- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts index b716aa61109..3852374d394 100644 --- a/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts @@ -13,7 +13,7 @@ import { ILanguageFeaturesService } from '../../../common/services/languageFeatu import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; @@ -61,3 +61,11 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { super.updateOptions(this._overwriteOptions); } } + +export function getOuterEditor(accessor: ServicesAccessor): ICodeEditor | null { + const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + if (editor instanceof EmbeddedCodeEditorWidget) { + return editor.getParentEditor(); + } + return editor; +} diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts index 175e4c8b49c..48ba268a699 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts @@ -14,7 +14,8 @@ import { Position } from '../../../../common/core/position.js'; import { Range } from '../../../../common/core/range.js'; import { IEditorContribution } from '../../../../common/editorCommon.js'; import { Location } from '../../../../common/languages.js'; -import { getOuterEditor, PeekContext } from '../../../peekView/browser/peekView.js'; +import { PeekContext } from '../../../peekView/browser/peekView.js'; +import { getOuterEditor } from '../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import * as nls from '../../../../../nls.js'; import { CommandsRegistry } from '../../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/editor/contrib/peekView/browser/peekView.ts b/src/vs/editor/contrib/peekView/browser/peekView.ts index 17084e7ae64..c774617241f 100644 --- a/src/vs/editor/contrib/peekView/browser/peekView.ts +++ b/src/vs/editor/contrib/peekView/browser/peekView.ts @@ -16,7 +16,6 @@ import * as objects from '../../../../base/common/objects.js'; import './media/peekViewWidget.css'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../browser/editorExtensions.js'; -import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EmbeddedCodeEditorWidget } from '../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; @@ -25,7 +24,7 @@ import * as nls from '../../../../nls.js'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator, IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { activeContrastBorder, contrastBorder, editorForeground, editorInfoForeground, registerColor } from '../../../../platform/theme/common/colorRegistry.js'; export const IPeekViewService = createDecorator('IPeekViewService'); @@ -79,14 +78,6 @@ class PeekContextController implements IEditorContribution { registerEditorContribution(PeekContextController.ID, PeekContextController, EditorContributionInstantiation.Eager); // eager because it needs to define a context key -export function getOuterEditor(accessor: ServicesAccessor): ICodeEditor | null { - const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); - if (editor instanceof EmbeddedCodeEditorWidget) { - return editor.getParentEditor(); - } - return editor; -} - export interface IPeekViewStyles extends IStyles { headerBackgroundColor?: Color; primaryHeadingColor?: Color; diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index 8ac67f6cd53..37ed63fb396 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -13,7 +13,7 @@ import { ISelectOptionItem } from '../../../../base/browser/ui/selectBox/selectB import { SelectActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { defaultSelectBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { getOuterEditor, peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from '../../../../editor/contrib/peekView/browser/peekView.js'; +import { peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from '../../../../editor/contrib/peekView/browser/peekView.js'; import { editorBackground } from '../../../../platform/theme/common/colorRegistry.js'; import { IMenu, IMenuService, MenuId, MenuItemAction, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; @@ -48,6 +48,7 @@ import { gotoNextLocation, gotoPreviousLocation } from '../../../../platform/the import { Codicon } from '../../../../base/common/codicons.js'; import { Color } from '../../../../base/common/color.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; +import { getOuterEditor } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; export const isQuickDiffVisible = new RawContextKey('dirtyDiffVisible', false); From 9cb7a27bd0bdecd16003711015d0a5172d5a8804 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 20 Dec 2024 11:14:30 +0100 Subject: [PATCH 429/479] Make Tab and Escape work when focus is in the preview --- .../browser/controller/commands.ts | 28 +++++++++++++++---- .../controller/inlineCompletionContextKeys.ts | 3 ++ .../controller/inlineCompletionsController.ts | 14 +++++++++- .../view/inlineEdits/sideBySideDiff.ts | 9 ++++-- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts index f183f9f896f..2fe48fc4eb9 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts @@ -11,7 +11,8 @@ import { Action2, MenuId } from '../../../../../platform/actions/common/actions. import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; -import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { KeybindingsRegistry, KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { INotificationService, Severity } from '../../../../../platform/notification/common/notification.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { EditorAction, EditorCommand, ServicesAccessor } from '../../../../browser/editorExtensions.js'; import { EditorContextKeys } from '../../../../common/editorContextKeys.js'; @@ -19,7 +20,6 @@ import { Context as SuggestContext } from '../../../suggest/browser/suggest.js'; import { inlineSuggestCommitId, showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId } from './commandIds.js'; import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js'; import { InlineCompletionsController } from './inlineCompletionsController.js'; -import { INotificationService, Severity } from '../../../../../platform/notification/common/notification.js'; export class ShowNextInlineSuggestionAction extends EditorAction { public static ID = showNextInlineSuggestionActionId; @@ -211,14 +211,21 @@ export class AcceptInlineCompletion extends EditorAction { }); } - public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { - const controller = InlineCompletionsController.get(editor); + public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = InlineCompletionsController.getInFocusedEditorOrParent(accessor); if (controller) { controller.model.get()?.accept(controller.editor); controller.editor.focus(); } } } +KeybindingsRegistry.registerKeybindingRule({ + id: inlineSuggestCommitId, + weight: 202, // greater than jump + primary: KeyCode.Tab, + when: ContextKeyExpr.and(InlineCompletionContextKeys.inInlineEditsPreviewEditor) +}); + export class JumpToNextInlineEdit extends EditorAction { constructor() { @@ -276,14 +283,23 @@ export class HideInlineCompletion extends EditorAction { }); } - public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { - const controller = InlineCompletionsController.get(editor); + public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + const controller = InlineCompletionsController.getInFocusedEditorOrParent(accessor); transaction(tx => { controller?.model.get()?.stop('explicitCancel', tx); }); + controller?.editor.focus(); } } +KeybindingsRegistry.registerKeybindingRule({ + id: HideInlineCompletion.ID, + weight: -1, // very weak + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(InlineCompletionContextKeys.inInlineEditsPreviewEditor) +}); + export class ToggleAlwaysShowInlineSuggestionToolbar extends Action2 { public static ID = 'editor.action.inlineSuggest.toggleAlwaysShowToolbar'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys.ts index 7fbd4dc19fc..834adac93ca 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys.ts @@ -5,6 +5,7 @@ import { RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; import { localize } from '../../../../../nls.js'; +import * as nls from '../../../../../nls.js'; export abstract class InlineCompletionContextKeys { @@ -19,4 +20,6 @@ export abstract class InlineCompletionContextKeys { public static readonly inlineEditVisible = new RawContextKey('inlineEditIsVisible', false, localize('inlineEditVisible', "Whether an inline edit is visible")); public static readonly tabShouldJumpToInlineEdit = new RawContextKey('tabShouldJumpToInlineEdit', false, localize('tabShouldJumpToInlineEdit', "Whether tab should jump to an inline edit.")); public static readonly tabShouldAcceptInlineEdit = new RawContextKey('tabShouldAcceptInlineEdit', false, localize('tabShouldAcceptInlineEdit', "Whether tab should accept the inline edit.")); + + public static readonly inInlineEditsPreviewEditor = new RawContextKey('inInlineEditsPreviewEditor', true, nls.localize('inInlineEditsPreviewEditor', "Whether the current code editor is showing an inline edits preview")); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 79fdfb85afd..b2b4b8ca6c6 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -16,7 +16,7 @@ import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../.. import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { hotClassGetOriginalInstance } from '../../../../../platform/observable/common/wrapInHotClass.js'; import { CoreEditingCommands } from '../../../../browser/coreCommands.js'; @@ -36,6 +36,7 @@ import { ObservableContextKeyService } from '../utils.js'; import { inlineSuggestCommitId } from './commandIds.js'; import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js'; import { InlineCompletionsView } from '../view/inlineCompletionsView.js'; +import { getOuterEditor } from '../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; export class InlineCompletionsController extends Disposable { private static readonly _instances = new Set(); @@ -43,6 +44,17 @@ export class InlineCompletionsController extends Disposable { public static hot = createHotClass(InlineCompletionsController); public static ID = 'editor.contrib.inlineCompletionsController'; + /** + * Find the controller in the focused editor or in the outer editor (if applicable) + */ + public static getInFocusedEditorOrParent(accessor: ServicesAccessor): InlineCompletionsController | null { + const outerEditor = getOuterEditor(accessor); + if (!outerEditor) { + return null; + } + return InlineCompletionsController.get(outerEditor); + } + public static get(editor: ICodeEditor): InlineCompletionsController | null { return hotClassGetOriginalInstance(editor.getContribution(InlineCompletionsController.ID)); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index b34dc44ac1d..042dc653cf9 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -28,11 +28,11 @@ import { Range } from '../../../../../common/core/range.js'; import { Command } from '../../../../../common/languages.js'; import { ITextModel } from '../../../../../common/model.js'; import { StickyScrollController } from '../../../../stickyScroll/browser/stickyScrollController.js'; +import { InlineCompletionContextKeys } from '../../controller/inlineCompletionContextKeys.js'; import { CustomizedMenuWorkbenchToolBar } from '../../hintsWidget/inlineCompletionsHintsWidget.js'; import { PathBuilder, StatusBarViewItem, getOffsetForPos, mapOutFalsy, maxContentWidthInRange, n } from './utils.js'; import { InlineEditWithChanges } from './viewAndDiffProducer.js'; - export const originalBackgroundColor = registerColor( 'inlineEdit.originalBackground', Color.transparent, @@ -271,7 +271,12 @@ export class InlineEditsSideBySideDiff extends Disposable { wordWrapOverride1: 'off', wordWrapOverride2: 'off', }, - { contributions: [], }, + { + contextKeyValues: { + [InlineCompletionContextKeys.inInlineEditsPreviewEditor.key]: true, + }, + contributions: [], + }, this._editor )); From 1730c76f6b3f2d6ab5819031cded0a6e24d54b6e Mon Sep 17 00:00:00 2001 From: zWing <371657110@qq.com> Date: Fri, 20 Dec 2024 18:29:31 +0800 Subject: [PATCH 430/479] fix(git-ext): fix limitWarning block the git status progress (#226577) --- extensions/git/src/repository.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 0c4b50cd5e5..9e6e0196abe 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -2359,24 +2359,26 @@ export class Repository implements Disposable { const yes = { title: l10n.t('Yes') }; const no = { title: l10n.t('No') }; - const result = await window.showWarningMessage(`${gitWarn} ${addKnown}`, yes, no, neverAgain); - if (result === yes) { - this.ignore([Uri.file(folderPath)]); - } else { + window.showWarningMessage(`${gitWarn} ${addKnown}`, yes, no, neverAgain).then(result => { + if (result === yes) { + this.ignore([Uri.file(folderPath)]); + } else { + if (result === neverAgain) { + config.update('ignoreLimitWarning', true, false); + } + + this.didWarnAboutLimit = true; + } + }); + } else { + const ok = { title: l10n.t('OK') }; + window.showWarningMessage(gitWarn, ok, neverAgain).then(result => { if (result === neverAgain) { config.update('ignoreLimitWarning', true, false); } this.didWarnAboutLimit = true; - } - } else { - const ok = { title: l10n.t('OK') }; - const result = await window.showWarningMessage(gitWarn, ok, neverAgain); - if (result === neverAgain) { - config.update('ignoreLimitWarning', true, false); - } - - this.didWarnAboutLimit = true; + }); } } From c1cff695d70848d892cc09908996e7f7a790e374 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:40:15 +0100 Subject: [PATCH 431/479] Consistent layout actions for views/containers/activitybar (#236686) consistent layout actions --- .../browser/actions/layoutActions.ts | 44 +++---------------- .../parts/activitybar/activitybarPart.ts | 25 ++++++----- .../parts/auxiliarybar/auxiliaryBarActions.ts | 4 +- .../browser/parts/paneCompositeBar.ts | 2 +- .../browser/parts/panel/panelActions.ts | 13 +----- .../views/browser/viewDescriptorService.ts | 8 +--- 6 files changed, 25 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 75253e235de..aed2fe31f74 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -181,17 +181,6 @@ MenuRegistry.appendMenuItems([{ when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 1 } -}, { - id: MenuId.ViewTitleContext, - item: { - group: '3_workbench_layout_move', - command: { - id: ToggleSidebarPositionAction.ID, - title: localize('move sidebar right', "Move Primary Side Bar Right") - }, - when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), - order: 1 - } }, { id: MenuId.ViewContainerTitleContext, item: { @@ -204,36 +193,25 @@ MenuRegistry.appendMenuItems([{ order: 1 } }, { - id: MenuId.ViewTitleContext, - item: { - group: '3_workbench_layout_move', - command: { - id: ToggleSidebarPositionAction.ID, - title: localize('move sidebar left', "Move Primary Side Bar Left") - }, - when: ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), - order: 1 - } -}, { - id: MenuId.ViewTitleContext, + id: MenuId.ViewContainerTitleContext, item: { group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, title: localize('move second sidebar left', "Move Secondary Side Bar Left") }, - when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar))), + when: ContextKeyExpr.and(ContextKeyExpr.notEquals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar))), order: 1 } }, { - id: MenuId.ViewTitleContext, + id: MenuId.ViewContainerTitleContext, item: { group: '3_workbench_layout_move', command: { id: ToggleSidebarPositionAction.ID, title: localize('move second sidebar right', "Move Secondary Side Bar Right") }, - when: ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar))), + when: ContextKeyExpr.and(ContextKeyExpr.equals('config.workbench.sideBar.location', 'right'), ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar))), order: 1 } }]); @@ -291,9 +269,10 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { // Toggle Sidebar Visibility -class ToggleSidebarVisibilityAction extends Action2 { +export class ToggleSidebarVisibilityAction extends Action2 { static readonly ID = 'workbench.action.toggleSidebarVisibility'; + static readonly LABEL = localize('compositePart.hideSideBarLabel', "Hide Primary Side Bar"); constructor() { super({ @@ -349,17 +328,6 @@ MenuRegistry.appendMenuItems([ when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), order: 2 } - }, { - id: MenuId.ViewTitleContext, - item: { - group: '3_workbench_layout_move', - command: { - id: ToggleSidebarVisibilityAction.ID, - title: localize('compositePart.hideSideBarLabel', "Hide Primary Side Bar"), - }, - when: ContextKeyExpr.and(SideBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar))), - order: 2 - } }, { id: MenuId.LayoutControlMenu, item: { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 0935255446a..f2af5fb3a2f 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -11,7 +11,7 @@ import { Part } from '../../part.js'; import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts, Position } from '../../../services/layout/browser/layoutService.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { ToggleSidebarPositionAction } from '../../actions/layoutActions.js'; +import { ToggleSidebarPositionAction, ToggleSidebarVisibilityAction } from '../../actions/layoutActions.js'; import { IThemeService, IColorTheme, registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER } from '../../../common/theme.js'; import { activeContrastBorder, contrastBorder, focusBorder } from '../../../../platform/theme/common/colorRegistry.js'; @@ -371,10 +371,16 @@ export class ActivityBarCompositeBar extends PaneCompositeBar { getActivityBarContextMenuActions(): IAction[] { const activityBarPositionMenu = this.menuService.getMenuActions(MenuId.ActivityBarPositionMenu, this.contextKeyService, { shouldForwardArgs: true, renderShortTitle: true }); const positionActions = getContextMenuActions(activityBarPositionMenu).secondary; - return [ + const actions = [ new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions), - toAction({ id: ToggleSidebarPositionAction.ID, label: ToggleSidebarPositionAction.getLabel(this.layoutService), run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarPositionAction().run(accessor)) }) + toAction({ id: ToggleSidebarPositionAction.ID, label: ToggleSidebarPositionAction.getLabel(this.layoutService), run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarPositionAction().run(accessor)) }), ]; + + if (this.part === Parts.SIDEBAR_PART) { + actions.push(toAction({ id: ToggleSidebarVisibilityAction.ID, label: ToggleSidebarVisibilityAction.LABEL, run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarVisibilityAction().run(accessor)) })); + } + + return actions; } } @@ -493,15 +499,10 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { MenuRegistry.appendMenuItem(MenuId.ViewContainerTitleContext, { submenu: MenuId.ActivityBarPositionMenu, title: localize('positionActivituBar', "Activity Bar Position"), - when: ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar)), - group: '3_workbench_layout_move', - order: 1 -}); - -MenuRegistry.appendMenuItem(MenuId.ViewTitleContext, { - submenu: MenuId.ActivityBarPositionMenu, - title: localize('positionActivituBar', "Activity Bar Position"), - when: ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar)), + when: ContextKeyExpr.or( + ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.Sidebar)), + ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar)) + ), group: '3_workbench_layout_move', order: 1 }); diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts index e564a8d49d7..e5cc8c36550 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions.ts @@ -149,14 +149,14 @@ MenuRegistry.appendMenuItems([ order: 2 } }, { - id: MenuId.ViewTitleContext, + id: MenuId.ViewContainerTitleContext, item: { group: '3_workbench_layout_move', command: { id: ToggleAuxiliaryBarAction.ID, title: localize2('hideAuxiliaryBar', 'Hide Secondary Side Bar'), }, - when: ContextKeyExpr.and(AuxiliaryBarVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar))), + when: ContextKeyExpr.and(AuxiliaryBarVisibleContext, ContextKeyExpr.equals('viewContainerLocation', ViewContainerLocationToString(ViewContainerLocation.AuxiliaryBar))), order: 2 } } diff --git a/src/vs/workbench/browser/parts/paneCompositeBar.ts b/src/vs/workbench/browser/parts/paneCompositeBar.ts index 05b43a296e1..89d07a87bc0 100644 --- a/src/vs/workbench/browser/parts/paneCompositeBar.ts +++ b/src/vs/workbench/browser/parts/paneCompositeBar.ts @@ -99,7 +99,7 @@ export class PaneCompositeBar extends Disposable { constructor( protected readonly options: IPaneCompositeBarOptions, - private readonly part: Parts, + protected readonly part: Parts, private readonly paneCompositePart: IPaneCompositePart, @IInstantiationService protected readonly instantiationService: IInstantiationService, @IStorageService private readonly storageService: IStorageService, diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index b3be54f49d7..6e43176a62e 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -14,7 +14,7 @@ import { ContextKeyExpr, ContextKeyExpression } from '../../../../platform/conte import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { ViewContainerLocationToString, ViewContainerLocation, IViewDescriptorService } from '../../../common/views.js'; +import { ViewContainerLocation, IViewDescriptorService } from '../../../common/views.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; @@ -326,17 +326,6 @@ MenuRegistry.appendMenuItems([ when: ContextKeyExpr.or(ContextKeyExpr.equals('config.workbench.layoutControl.type', 'toggles'), ContextKeyExpr.equals('config.workbench.layoutControl.type', 'both')), order: 1 } - }, { - id: MenuId.ViewTitleContext, - item: { - group: '3_workbench_layout_move', - command: { - id: TogglePanelAction.ID, - title: localize2('hidePanel', 'Hide Panel'), - }, - when: ContextKeyExpr.and(PanelVisibleContext, ContextKeyExpr.equals('viewLocation', ViewContainerLocationToString(ViewContainerLocation.Panel))), - order: 2 - } } ]); diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index ad59c3e2a2a..c667e719ae4 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -792,16 +792,12 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor order: index, }, { id: MenuId.ViewContainerTitleContext, - when: ContextKeyExpr.and( - ContextKeyExpr.equals('viewContainer', viewContainerModel.viewContainer.id), - ), + when: ContextKeyExpr.equals('viewContainer', viewContainerModel.viewContainer.id), order: index, group: '1_toggleVisibility' }, { id: MenuId.ViewTitleContext, - when: ContextKeyExpr.and( - ContextKeyExpr.or(...viewContainerModel.visibleViewDescriptors.map(v => ContextKeyExpr.equals('view', v.id))) - ), + when: ContextKeyExpr.or(...viewContainerModel.visibleViewDescriptors.map(v => ContextKeyExpr.equals('view', v.id))), order: index, group: '2_toggleVisibility' }] From d239347a1c75707cfa7d9b4d9bfe5b7f28277c37 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 20 Dec 2024 12:05:21 +0100 Subject: [PATCH 432/479] Add a context key for whether commenting is enabled (#236679) --- .../workbench/contrib/comments/browser/commentService.ts | 3 +++ .../contrib/comments/common/commentContextKeys.ts | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index ca5a8aa59d9..b7d1cde9aea 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -167,6 +167,7 @@ export class CommentService extends Disposable implements ICommentService { private _commentMenus = new Map(); private _isCommentingEnabled: boolean = true; private _workspaceHasCommenting: IContextKey; + private _commentingEnabled: IContextKey; private _continueOnComments = new Map(); // uniqueOwner -> PendingCommentThread[] private _continueOnCommentProviders = new Set(); @@ -190,6 +191,7 @@ export class CommentService extends Disposable implements ICommentService { this._handleConfiguration(); this._handleZenMode(); this._workspaceHasCommenting = CommentContextKeys.WorkspaceHasCommenting.bindTo(contextKeyService); + this._commentingEnabled = CommentContextKeys.commentingEnabled.bindTo(contextKeyService); const storageListener = this._register(new DisposableStore()); const storageEvent = Event.debounce(this.storageService.onDidChangeValue(StorageScope.WORKSPACE, CONTINUE_ON_COMMENTS, storageListener), (last, event) => last?.external ? last : event, 500); @@ -277,6 +279,7 @@ export class CommentService extends Disposable implements ICommentService { enableCommenting(enable: boolean): void { if (enable !== this._isCommentingEnabled) { this._isCommentingEnabled = enable; + this._commentingEnabled.set(enable); this._onDidChangeCommentingEnabled.fire(enable); } } diff --git a/src/vs/workbench/contrib/comments/common/commentContextKeys.ts b/src/vs/workbench/contrib/comments/common/commentContextKeys.ts index 210465efe6f..2a5d0776c45 100644 --- a/src/vs/workbench/contrib/comments/common/commentContextKeys.ts +++ b/src/vs/workbench/contrib/comments/common/commentContextKeys.ts @@ -66,4 +66,12 @@ export namespace CommentContextKeys { * The comment widget is focused. */ export const commentFocused = new RawContextKey('commentFocused', false, { type: 'boolean', description: nls.localize('commentFocused', "Set when the comment is focused") }); + + /** + * A context key that is set when commenting is enabled. + */ + export const commentingEnabled = new RawContextKey('commentingEnabled', true, { + description: nls.localize('commentingEnabled', "Whether commenting functionality is enabled"), + type: 'boolean' + }); } From 692fbdbd4d72edbd6bb2507f6a71d320f3a12647 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 04:52:33 -0800 Subject: [PATCH 433/479] Set multi-line setting to a valid value when disabling Fixes #231562 --- .../terminalContrib/clipboard/browser/terminalClipboard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/clipboard/browser/terminalClipboard.ts b/src/vs/workbench/contrib/terminalContrib/clipboard/browser/terminalClipboard.ts index ae9dc0b6699..e8ba759f61f 100644 --- a/src/vs/workbench/contrib/terminalContrib/clipboard/browser/terminalClipboard.ts +++ b/src/vs/workbench/contrib/terminalContrib/clipboard/browser/terminalClipboard.ts @@ -95,7 +95,7 @@ export async function shouldPasteTerminalText(accessor: ServicesAccessor, text: } if (result.confirmed && checkboxChecked) { - await configurationService.updateValue(TerminalSettingId.EnableMultiLinePasteWarning, false); + await configurationService.updateValue(TerminalSettingId.EnableMultiLinePasteWarning, 'never'); } if (result.singleLine) { From 78cd9519c5c7b2b8ddb0a092e658d240db6bcbe0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 05:10:57 -0800 Subject: [PATCH 434/479] Close the terminal view when there's only one visible view Part of #236517 --- .../workbench/contrib/terminal/browser/terminalGroupService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts index ff4c4103eba..6f5040cd4d7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts @@ -83,7 +83,7 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe hidePanel(): void { // Hide the panel if the terminal is in the panel and it has no sibling views const panel = this._viewDescriptorService.getViewContainerByViewId(TERMINAL_VIEW_ID); - if (panel && this._viewDescriptorService.getViewContainerModel(panel).activeViewDescriptors.length === 1) { + if (panel && this._viewDescriptorService.getViewContainerModel(panel).visibleViewDescriptors.length === 1) { this._viewsService.closeView(TERMINAL_VIEW_ID); TerminalContextKeys.tabsMouse.bindTo(this._contextKeyService).set(false); } From 2dc420d6387896b49298a05288704ccf75220271 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 05:31:07 -0800 Subject: [PATCH 435/479] Create terminal when toggled without contents Fixes #236517 --- .../contrib/terminal/browser/terminalView.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index b71064b87f0..38e6d342942 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -57,6 +57,11 @@ export class TerminalViewPane extends ViewPane { private _terminalTabbedView?: TerminalTabbedView; get terminalTabbedView(): TerminalTabbedView | undefined { return this._terminalTabbedView; } private _isInitialized: boolean = false; + /** + * Tracks an active promise of terminal creation requested by this component. This helps prevent + * double creation for example when toggling a terminal's visibility and focusing it. + */ + private _isTerminalBeingCreated: boolean = false; private readonly _newDropdown: MutableDisposable = this._register(new MutableDisposable()); private readonly _dropdownMenu: IMenu; private readonly _singleTabMenu: IMenu; @@ -164,7 +169,8 @@ export class TerminalViewPane extends ViewPane { if (!wasInitialized) { switch (hideOnStartup) { case 'never': - this._terminalService.createTerminal({ location: TerminalLocation.Panel }); + this._isTerminalBeingCreated = true; + this._terminalService.createTerminal({ location: TerminalLocation.Panel }).finally(() => this._isTerminalBeingCreated = false); break; case 'whenEmpty': if (this._terminalService.restoredGroupCount === 0) { @@ -175,7 +181,10 @@ export class TerminalViewPane extends ViewPane { return; } - this._terminalService.createTerminal({ location: TerminalLocation.Panel }); + if (!this._isTerminalBeingCreated) { + this._isTerminalBeingCreated = true; + this._terminalService.createTerminal({ location: TerminalLocation.Panel }).finally(() => this._isTerminalBeingCreated = false); + } } } @@ -320,6 +329,10 @@ export class TerminalViewPane extends ViewPane { override focus() { super.focus(); if (this._terminalService.connectionState === TerminalConnectionState.Connected) { + if (this._terminalGroupService.instances.length === 0 && !this._isTerminalBeingCreated) { + this._isTerminalBeingCreated = true; + this._terminalService.createTerminal({ location: TerminalLocation.Panel }).finally(() => this._isTerminalBeingCreated = false); + } this._terminalGroupService.showPanel(true); return; } From 8d0d731b7c53b7656e9a7014b14b797dd1796a1a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:31:20 +0100 Subject: [PATCH 436/479] SCM - add scm.historyProviderCount context key (#236694) --- .../contrib/scm/browser/scm.contribution.ts | 12 ++----- .../contrib/scm/common/scmService.ts | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 0c2d7c1af5f..09ecabe67ee 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -38,8 +38,6 @@ import { IViewsService } from '../../../services/views/common/viewsService.js'; import { IListService, WorkbenchList } from '../../../../platform/list/browser/listService.js'; import { isSCMRepository } from './util.js'; import { SCMHistoryViewPane } from './scmHistoryViewPane.js'; -import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js'; -import { RemoteNameContext } from '../../../common/contextkeys.js'; import { QuickDiffModelService, IQuickDiffModelService } from './quickDiffModel.js'; import { QuickDiffEditorController } from './quickDiffWidget.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js'; @@ -137,14 +135,8 @@ viewsRegistry.registerViews([{ weight: 40, order: 2, when: ContextKeyExpr.and( - // Repository Count - ContextKeyExpr.and( - ContextKeyExpr.has('scm.providerCount'), - ContextKeyExpr.notEquals('scm.providerCount', 0)), - // Not Serverless - ContextKeyExpr.and( - IsWebContext, - RemoteNameContext.isEqualTo(''))?.negate() + ContextKeyExpr.has('scm.historyProviderCount'), + ContextKeyExpr.notEquals('scm.historyProviderCount', 0), ), containerIcon: sourceControlViewIcon }], viewContainer); diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index c9af734400a..14c69f73b0d 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator, ISCMInputChangeEvent, SCMInputChangeReason, InputValidationType, IInputValidation } from './scm.js'; import { ILogService } from '../../../../platform/log/common/log.js'; @@ -17,6 +17,7 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { Schemas } from '../../../../base/common/network.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +import { runOnChange } from '../../../../base/common/observable.js'; class SCMInput extends Disposable implements ISCMInput { @@ -188,7 +189,7 @@ class SCMRepository implements ISCMRepository { constructor( public readonly id: string, public readonly provider: ISCMProvider, - private disposable: IDisposable, + private readonly disposables: DisposableStore, inputHistory: SCMInputHistory ) { this.input = new SCMInput(this, inputHistory); @@ -204,7 +205,7 @@ class SCMRepository implements ISCMRepository { } dispose(): void { - this.disposable.dispose(); + this.disposables.dispose(); this.provider.dispose(); } } @@ -355,6 +356,7 @@ export class SCMService implements ISCMService { private inputHistory: SCMInputHistory; private providerCount: IContextKey; + private historyProviderCount: IContextKey; private readonly _onDidAddProvider = new Emitter(); readonly onDidAddRepository: Event = this._onDidAddProvider.event; @@ -370,7 +372,9 @@ export class SCMService implements ISCMService { @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { this.inputHistory = new SCMInputHistory(storageService, workspaceContextService); + this.providerCount = contextKeyService.createKey('scm.providerCount', 0); + this.historyProviderCount = contextKeyService.createKey('scm.historyProviderCount', 0); } registerSCMProvider(provider: ISCMProvider): ISCMRepository { @@ -380,17 +384,33 @@ export class SCMService implements ISCMService { throw new Error(`SCM Provider ${provider.id} already exists.`); } - const disposable = toDisposable(() => { + const disposables = new DisposableStore(); + + const historyProviderCount = () => { + return Array.from(this._repositories.values()) + .filter(r => !!r.provider.historyProvider).length; + }; + + disposables.add(toDisposable(() => { this._repositories.delete(provider.id); this._onDidRemoveProvider.fire(repository); + this.providerCount.set(this._repositories.size); - }); + this.historyProviderCount.set(historyProviderCount()); + })); - const repository = new SCMRepository(provider.id, provider, disposable, this.inputHistory); + const repository = new SCMRepository(provider.id, provider, disposables, this.inputHistory); this._repositories.set(provider.id, repository); - this._onDidAddProvider.fire(repository); + + disposables.add(runOnChange(provider.historyProvider, () => { + this.historyProviderCount.set(historyProviderCount()); + })); this.providerCount.set(this._repositories.size); + this.historyProviderCount.set(historyProviderCount()); + + this._onDidAddProvider.fire(repository); + return repository; } From f7fd6660f9f11e5a270fee834ca0382416ba9dfe Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 05:35:12 -0800 Subject: [PATCH 437/479] Add hideOnLastClosed setting Fixes #236517 --- src/vs/platform/terminal/common/terminal.ts | 1 + src/vs/workbench/contrib/terminal/browser/terminalService.ts | 2 +- src/vs/workbench/contrib/terminal/common/terminal.ts | 1 + .../contrib/terminal/common/terminalConfiguration.ts | 5 +++++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 8235e8a0ab4..246ccaf6a76 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -103,6 +103,7 @@ export const enum TerminalSettingId { EnablePersistentSessions = 'terminal.integrated.enablePersistentSessions', PersistentSessionReviveProcess = 'terminal.integrated.persistentSessionReviveProcess', HideOnStartup = 'terminal.integrated.hideOnStartup', + HideOnLastClosed = 'terminal.integrated.hideOnLastClosed', CustomGlyphs = 'terminal.integrated.customGlyphs', RescaleOverlappingGlyphs = 'terminal.integrated.rescaleOverlappingGlyphs', PersistentSessionScrollback = 'terminal.integrated.persistentSessionScrollback', diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 698559d3b2c..7fea79a51a7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -208,7 +208,7 @@ export class TerminalService extends Disposable implements ITerminalService { // down. When shutting down the panel is locked in place so that it is restored upon next // launch. this._register(this._terminalGroupService.onDidChangeActiveInstance(instance => { - if (!instance && !this._isShuttingDown) { + if (!instance && !this._isShuttingDown && this._terminalConfigService.config.hideOnLastClosed) { this._terminalGroupService.hidePanel(); } if (instance?.shellType) { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index cf5c306bb70..6fbd41d8637 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -211,6 +211,7 @@ export interface ITerminalConfiguration { experimental?: { windowsUseConptyDll?: boolean; }; + hideOnLastClosed: boolean; } export interface ITerminalFont { diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index b90a67cbbc6..6743ad25d3c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -549,6 +549,11 @@ const terminalConfiguration: IConfigurationNode = { ], default: 'never' }, + [TerminalSettingId.HideOnLastClosed]: { + description: localize('terminal.integrated.hideOnLastClosed', "Whether to hide the terminal view when the last terminal is closed. This will only happen when the terminal is the only visible view in the view container."), + type: 'boolean', + default: true + }, [TerminalSettingId.CustomGlyphs]: { markdownDescription: localize('terminal.integrated.customGlyphs', "Whether to draw custom glyphs for block element and box drawing characters instead of using the font, which typically yields better rendering with continuous lines. Note that this doesn't work when {0} is disabled.", `\`#${TerminalSettingId.GpuAcceleration}#\``), type: 'boolean', From 502a7e5d43b277bd4b8783f837a7cba65234e4f4 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:16:06 +0100 Subject: [PATCH 438/479] SCM - do not show "Open in External Terminal" action when connected to a remote (#236697) --- .../contrib/scm/browser/scm.contribution.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 09ecabe67ee..7f8e9c71a09 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -41,6 +41,7 @@ import { SCMHistoryViewPane } from './scmHistoryViewPane.js'; import { QuickDiffModelService, IQuickDiffModelService } from './quickDiffModel.js'; import { QuickDiffEditorController } from './quickDiffWidget.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js'; +import { RemoteNameContext } from '../../../common/contextkeys.js'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -529,7 +530,12 @@ MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { id: 'scm.openInTerminal', title: localize('open in external terminal', "Open in External Terminal") }, - when: ContextKeyExpr.and(ContextKeyExpr.equals('scmProviderHasRootUri', true), ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'external'), ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) + when: ContextKeyExpr.and( + RemoteNameContext.isEqualTo(''), + ContextKeyExpr.equals('scmProviderHasRootUri', true), + ContextKeyExpr.or( + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'external'), + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) }); MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { @@ -538,7 +544,11 @@ MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { id: 'scm.openInIntegratedTerminal', title: localize('open in integrated terminal', "Open in Integrated Terminal") }, - when: ContextKeyExpr.and(ContextKeyExpr.equals('scmProviderHasRootUri', true), ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'integrated'), ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) + when: ContextKeyExpr.and( + ContextKeyExpr.equals('scmProviderHasRootUri', true), + ContextKeyExpr.or( + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'integrated'), + ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) }); KeybindingsRegistry.registerCommandAndKeybindingRule({ From d8c2678e9a4153f56a4160481b1c983558637b6a Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:25:40 +0100 Subject: [PATCH 439/479] Add share context menu action (#236699) share context menu action --- src/vs/workbench/browser/parts/titlebar/titlebarActions.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index f84bf3f06b7..e4b8a3e6b9d 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -64,6 +64,12 @@ registerAction2(class ToggleNavigationControl extends ToggleTitleBarConfigAction } }); +registerAction2(class ToggleShareControl extends ToggleTitleBarConfigAction { + constructor() { + super('workbench.experimental.share.enabled', localize('toggle.share', 'Share'), localize('toggle.shareDescription', "Toggle visibility of the Share action in title bar"), 2, false, ContextKeyExpr.has('config.window.commandCenter')); + } +}); + registerAction2(class ToggleLayoutControl extends ToggleTitleBarConfigAction { constructor() { super(LayoutSettings.LAYOUT_ACTIONS, localize('toggle.layout', 'Layout Controls'), localize('toggle.layoutDescription', "Toggle visibility of the Layout Controls in title bar"), 3, true); From b8b4db329e93816f34611d1a653057c323485463 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:51:58 +0100 Subject: [PATCH 440/479] Git - cleanup `vscode-merge-base` git config key when deleteing a branch (#236716) --- extensions/git/src/git.ts | 6 +++--- extensions/git/src/repository.ts | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 87ea23e3a85..62bae1422df 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1166,11 +1166,11 @@ export class Repository { return this.git.spawn(args, options); } - async config(scope: string, key: string, value: any = null, options: SpawnOptions = {}): Promise { - const args = ['config']; + async config(command: string, scope: string, key: string, value: any = null, options: SpawnOptions = {}): Promise { + const args = ['config', command]; if (scope) { - args.push('--' + scope); + args.push(`--${scope}`); } args.push(key); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 9e6e0196abe..ec66d510c72 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -1082,15 +1082,15 @@ export class Repository implements Disposable { } getConfig(key: string): Promise { - return this.run(Operation.Config(true), () => this.repository.config('local', key)); + return this.run(Operation.Config(true), () => this.repository.config('get', 'local', key)); } getGlobalConfig(key: string): Promise { - return this.run(Operation.Config(true), () => this.repository.config('global', key)); + return this.run(Operation.Config(true), () => this.repository.config('get', 'global', key)); } setConfig(key: string, value: string): Promise { - return this.run(Operation.Config(false), () => this.repository.config('local', key, value)); + return this.run(Operation.Config(false), () => this.repository.config('set', 'local', key, value)); } log(options?: LogOptions & { silent?: boolean }): Promise { @@ -1465,7 +1465,10 @@ export class Repository implements Disposable { } async deleteBranch(name: string, force?: boolean): Promise { - await this.run(Operation.DeleteBranch, () => this.repository.deleteBranch(name, force)); + return this.run(Operation.DeleteBranch, async () => { + await this.repository.deleteBranch(name, force); + await this.repository.config('unset', 'local', `branch.${name}.vscode-merge-base`); + }); } async renameBranch(name: string): Promise { From 5cdf4dd74bae97e2701de972294aa3e501c01920 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:14:37 -0800 Subject: [PATCH 441/479] Register editor gpu acceleration with included:false Fixes #235730 --- src/vs/editor/common/config/editorOptions.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index e355a960db9..5fe2028e366 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -5833,15 +5833,14 @@ export const EditorOptions = { EditorOption.experimentalGpuAcceleration, 'experimentalGpuAcceleration', 'off' as 'off' | 'on', ['off', 'on'] as const, - undefined - // TODO: Uncomment when we want to expose the setting to VS Code users - // { - // enumDescriptions: [ - // nls.localize('experimentalGpuAcceleration.off', "Use regular DOM-based rendering."), - // nls.localize('experimentalGpuAcceleration.on', "Use GPU acceleration."), - // ], - // description: nls.localize('experimentalGpuAcceleration', "Controls whether to use the (very) experimental GPU acceleration to render the editor.") - // } + { + included: false, // Hide the setting from users while it's unstable + enumDescriptions: [ + nls.localize('experimentalGpuAcceleration.off', "Use regular DOM-based rendering."), + nls.localize('experimentalGpuAcceleration.on', "Use GPU acceleration."), + ], + description: nls.localize('experimentalGpuAcceleration', "Controls whether to use the (very) experimental GPU acceleration to render the editor.") + } )), experimentalWhitespaceRendering: register(new EditorStringEnumOption( EditorOption.experimentalWhitespaceRendering, 'experimentalWhitespaceRendering', From decaa08e9945fad06912b5aafbd8f0f0e88e11c2 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 20 Dec 2024 17:15:05 +0100 Subject: [PATCH 442/479] Use correct context keys for share provider title context action (#236718) use correct context keys for share provider titile context action --- .../browser/parts/titlebar/titlebarActions.ts | 8 +------- .../contrib/chat/browser/actions/chatActions.ts | 2 +- .../workbench/contrib/share/browser/shareService.ts | 12 ++++++++++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index e4b8a3e6b9d..e09172e4a7f 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -64,15 +64,9 @@ registerAction2(class ToggleNavigationControl extends ToggleTitleBarConfigAction } }); -registerAction2(class ToggleShareControl extends ToggleTitleBarConfigAction { - constructor() { - super('workbench.experimental.share.enabled', localize('toggle.share', 'Share'), localize('toggle.shareDescription', "Toggle visibility of the Share action in title bar"), 2, false, ContextKeyExpr.has('config.window.commandCenter')); - } -}); - registerAction2(class ToggleLayoutControl extends ToggleTitleBarConfigAction { constructor() { - super(LayoutSettings.LAYOUT_ACTIONS, localize('toggle.layout', 'Layout Controls'), localize('toggle.layoutDescription', "Toggle visibility of the Layout Controls in title bar"), 3, true); + super(LayoutSettings.LAYOUT_ACTIONS, localize('toggle.layout', 'Layout Controls'), localize('toggle.layoutDescription', "Toggle visibility of the Layout Controls in title bar"), 4, true); } }); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index d1d7d951559..f0ece5fa2b9 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -543,7 +543,7 @@ registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction { super( 'chat.commandCenter.enabled', localize('toggle.chatControl', 'Copilot Controls'), - localize('toggle.chatControlsDescription', "Toggle visibility of the Copilot Controls in title bar"), 4, false, + localize('toggle.chatControlsDescription', "Toggle visibility of the Copilot Controls in title bar"), 5, false, ContextKeyExpr.and( ChatContextKeys.supported, ContextKeyExpr.has('config.window.commandCenter') diff --git a/src/vs/workbench/contrib/share/browser/shareService.ts b/src/vs/workbench/contrib/share/browser/shareService.ts index 0d684acf736..9a94bd370be 100644 --- a/src/vs/workbench/contrib/share/browser/shareService.ts +++ b/src/vs/workbench/contrib/share/browser/shareService.ts @@ -9,11 +9,13 @@ import { URI } from '../../../../base/common/uri.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { score } from '../../../../editor/common/languageSelector.js'; import { localize } from '../../../../nls.js'; -import { ISubmenuItem } from '../../../../platform/actions/common/actions.js'; -import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { ISubmenuItem, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { ToggleTitleBarConfigAction } from '../../../browser/parts/titlebar/titlebarActions.js'; +import { WorkspaceFolderCountContext } from '../../../common/contextkeys.js'; import { IShareProvider, IShareService, IShareableItem } from '../common/share.js'; export const ShareProviderCountContext = new RawContextKey('shareProviderCount', 0, localize('shareProviderCount', "The number of available share providers")); @@ -84,3 +86,9 @@ export class ShareService implements IShareService { return; } } + +registerAction2(class ToggleShareControl extends ToggleTitleBarConfigAction { + constructor() { + super('workbench.experimental.share.enabled', localize('toggle.share', 'Share'), localize('toggle.shareDescription', "Toggle visibility of the Share action in title bar"), 3, false, ContextKeyExpr.and(ContextKeyExpr.has('config.window.commandCenter'), ContextKeyExpr.and(ShareProviderCountContext.notEqualsTo(0), WorkspaceFolderCountContext.notEqualsTo(0)))); + } +}); From 62948ea6d834ffed42b20fc0cba28ffaa3cf0cbe Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:23:25 -0800 Subject: [PATCH 443/479] Indicate when terminal sticky scroll is truncated Fixes #199974 --- .../stickyScroll/browser/terminalStickyScrollOverlay.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts index 7b3d3de3371..0da38ee5994 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts @@ -265,6 +265,7 @@ export class TerminalStickyScrollOverlay extends Disposable { const rowOffset = !isPartialCommand && command.endMarker ? Math.max(buffer.viewportY - command.endMarker.line + 1, 0) : 0; const maxLineCount = Math.min(this._rawMaxLineCount, Math.floor(xterm.rows * Constants.StickyScrollPercentageCap)); const stickyScrollLineCount = Math.min(promptRowCount + commandRowCount - 1, maxLineCount) - rowOffset; + const isTruncated = stickyScrollLineCount < promptRowCount + commandRowCount - 1; // Hide sticky scroll if it's currently on a line that contains it if (buffer.viewportY <= stickyScrollLineStart) { @@ -293,7 +294,7 @@ export class TerminalStickyScrollOverlay extends Disposable { start: stickyScrollLineStart + rowOffset, end: stickyScrollLineStart + rowOffset + Math.max(stickyScrollLineCount - 1, 0) } - }); + }) + (isTruncated ? '\x1b[0m …' : ''); // If a partial command's sticky scroll would show nothing, just hide it. This is another // edge case when using a pager or interactive editor. From 12708746496225474c020218d3eae298977266f5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:43:35 -0800 Subject: [PATCH 444/479] Suppress exit warning after ctrl+d Fixes #160325 --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 5 +++++ .../workbench/contrib/terminal/browser/terminalInstance.ts | 2 +- .../contrib/terminal/browser/xterm/xtermTerminal.ts | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 2a56aeca979..ead75598c0e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -1095,6 +1095,11 @@ export interface IXtermTerminal extends IDisposable { */ readonly isGpuAccelerated: boolean; + /** + * The last `onData` input event fired by {@link RawXtermTerminal.onData}. + */ + readonly lastInputEvent: string | undefined; + /** * Attached the terminal to the given element * @param container Container the terminal will be rendered in diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 0a1646792a0..df286dd974a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1598,7 +1598,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } else { if (exitMessage) { const failedDuringLaunch = this._processManager.processState === ProcessState.KilledDuringLaunch; - if (failedDuringLaunch || this._terminalConfigurationService.config.showExitAlert) { + if (failedDuringLaunch || (this._terminalConfigurationService.config.showExitAlert && this.xterm?.lastInputEvent !== /*Ctrl+D*/'\x04')) { // Always show launch failures this._notificationService.notify({ message: exitMessage, diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 5ef6d497196..4e860821f7e 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -96,6 +96,8 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach private static _suggestedRendererType: 'dom' | undefined = undefined; private _attached?: { container: HTMLElement; options: IXtermAttachToElementOptions }; private _isPhysicalMouseWheel = MouseWheelClassifier.INSTANCE.isPhysicalMouseWheel(); + private _lastInputEvent: string | undefined; + get lastInputEvent(): string | undefined { return this._lastInputEvent; } // Always on addons private _markNavigationAddon: MarkNavigationAddon; @@ -256,6 +258,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach this._anyFocusedTerminalHasSelection.set(this.raw.hasSelection()); } })); + this._register(this.raw.onData(e => this._lastInputEvent = e)); // Load addons this._updateUnicodeVersion(); From 1be76b3c97bbe6ce32822c2e7baac4d115376478 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 20 Dec 2024 09:47:26 -0800 Subject: [PATCH 445/479] Fix response toString for inline refs Fixes #236655 --- .../contrib/chat/common/chatModel.ts | 98 +++++++++++++------ .../Response_inline_reference.0.snap | 4 +- .../chat/test/common/chatModel.test.ts | 9 +- 3 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 14fa03dffa0..3cf9a8585c7 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -9,6 +9,7 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { IMarkdownString, MarkdownString, isMarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { revive } from '../../../../base/common/marshalling.js'; +import { Schemas } from '../../../../base/common/network.js'; import { equals } from '../../../../base/common/objects.js'; import { basename, isEqual } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; @@ -397,38 +398,12 @@ export class Response extends Disposable implements IResponse { } private _updateRepr(quiet?: boolean) { - const inlineRefToRepr = (part: IChatContentInlineReference) => - 'uri' in part.inlineReference - ? basename(part.inlineReference.uri) - : 'name' in part.inlineReference - ? part.inlineReference.name - : basename(part.inlineReference); - - this._responseRepr = this._responseParts.map(part => { - if (part.kind === 'treeData') { - return ''; - } else if (part.kind === 'inlineReference') { - return inlineRefToRepr(part); - } else if (part.kind === 'command') { - return part.command.title; - } else if (part.kind === 'textEditGroup') { - return localize('editsSummary', "Made changes."); - } else if (part.kind === 'progressMessage' || part.kind === 'codeblockUri' || part.kind === 'toolInvocation' || part.kind === 'toolInvocationSerialized') { - return ''; - } else if (part.kind === 'confirmation') { - return `${part.title}\n${part.message}`; - } else { - return part.content.value; - } - }) - .filter(s => s.length > 0) - .join('\n\n'); - + this._responseRepr = this.partsToRepr(this._responseParts); this._responseRepr += this._citations.length ? '\n\n' + getCodeCitationsMessage(this._citations) : ''; this._markdownContent = this._responseParts.map(part => { if (part.kind === 'inlineReference') { - return inlineRefToRepr(part); + return this.inlineRefToRepr(part); } else if (part.kind === 'markdownContent' || part.kind === 'markdownVuln') { return part.content.value; } else { @@ -442,6 +417,73 @@ export class Response extends Disposable implements IResponse { this._onDidChangeValue.fire(); } } + + private partsToRepr(parts: readonly IChatProgressResponseContent[]): string { + const blocks: string[] = []; + let currentBlockSegments: string[] = []; + + for (const part of parts) { + let segment: { text: string; isBlock?: boolean } | undefined; + switch (part.kind) { + case 'treeData': + case 'progressMessage': + case 'codeblockUri': + case 'toolInvocation': + case 'toolInvocationSerialized': + // Ignore + continue; + case 'inlineReference': + segment = { text: this.inlineRefToRepr(part) }; + break; + case 'command': + segment = { text: part.command.title, isBlock: true }; + break; + case 'textEditGroup': + segment = { text: localize('editsSummary', "Made changes."), isBlock: true }; + break; + case 'confirmation': + segment = { text: `${part.title}\n${part.message}`, isBlock: true }; + break; + default: + segment = { text: part.content.value }; + break; + } + + if (segment.isBlock) { + if (currentBlockSegments.length) { + blocks.push(currentBlockSegments.join('')); + currentBlockSegments = []; + } + blocks.push(segment.text); + } else { + currentBlockSegments.push(segment.text); + } + } + + if (currentBlockSegments.length) { + blocks.push(currentBlockSegments.join('')); + } + + return blocks.join('\n\n'); + } + + private inlineRefToRepr(part: IChatContentInlineReference) { + if ('uri' in part.inlineReference) { + return this.uriToRepr(part.inlineReference.uri); + } + + return 'name' in part.inlineReference + ? '`' + part.inlineReference.name + '`' + : this.uriToRepr(part.inlineReference); + } + + private uriToRepr(uri: URI): string { + if (uri.scheme === Schemas.http || uri.scheme === Schemas.https) { + return uri.toString(false); + } + + return basename(uri); + } } export class ChatResponseModel extends Disposable implements IChatResponseModel { diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap index b26d84334a0..3a54719571d 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap @@ -1,7 +1,7 @@ [ { content: { - value: "text before", + value: "text before ", isTrusted: false, supportThemeIcons: false, supportHtml: false @@ -14,7 +14,7 @@ }, { content: { - value: "text after", + value: " text after", isTrusted: false, supportThemeIcons: false, supportHtml: false diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index be40ba599bd..c9c2059b307 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -191,10 +191,13 @@ suite('Response', () => { test('inline reference', async () => { const response = store.add(new Response([])); - response.updateContent({ content: new MarkdownString('text before'), kind: 'markdownContent' }); - response.updateContent({ inlineReference: URI.parse('https://microsoft.com'), kind: 'inlineReference' }); - response.updateContent({ content: new MarkdownString('text after'), kind: 'markdownContent' }); + response.updateContent({ content: new MarkdownString('text before '), kind: 'markdownContent' }); + response.updateContent({ inlineReference: URI.parse('https://microsoft.com/'), kind: 'inlineReference' }); + response.updateContent({ content: new MarkdownString(' text after'), kind: 'markdownContent' }); await assertSnapshot(response.value); + + assert.strictEqual(response.toString(), 'text before https://microsoft.com/ text after'); + }); }); From 437450a35c25538b5a8719aeee03d060be883f0c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:08:18 -0800 Subject: [PATCH 446/479] Explain exitCode undefined in more detail Fixes #236670 --- src/vscode-dts/vscode.d.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index c9ecff2b0b9..7edf86d0780 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -7724,10 +7724,17 @@ declare module 'vscode' { /** * The exit code reported by the shell. * - * Note that `undefined` means the shell either did not report an exit code (ie. the shell - * integration script is misbehaving) or the shell reported a command started before the command - * finished (eg. a sub-shell was opened). Generally this should not happen, depending on the use - * case, it may be best to treat this as a failure. + * When this is `undefined` it can mean several things: + * + * - The shell either did not report an exit code (ie. the shell integration script is + * misbehaving) + * - The shell reported a command started before the command finished (eg. a sub-shell was + * opened). + * - The user canceled the command via ctrl+c. + * - The user pressed enter when there was no input. + * + * Generally this should not happen. Depending on the use case, it may be best to treat this + * as a failure. * * @example * const execution = shellIntegration.executeCommand({ From dfdece69e67e45a2a563929608c69dad8de6fe6e Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 20 Dec 2024 12:23:36 -0600 Subject: [PATCH 447/479] fix types for task API props (#236735) fix #231858 --- src/vs/workbench/api/common/extHostTypes.ts | 6 ++++-- src/vscode-dts/vscode.d.ts | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 90f82a69ce0..d388236e837 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2343,7 +2343,9 @@ export class ShellExecution implements vscode.ShellExecution { throw illegalArgument('command'); } this._command = arg0; - this._args = arg1 as (string | vscode.ShellQuotedString)[]; + if (arg1) { + this._args = arg1; + } this._options = arg2; } else { if (typeof arg0 !== 'string') { @@ -2380,7 +2382,7 @@ export class ShellExecution implements vscode.ShellExecution { return this._args; } - set args(value: (string | vscode.ShellQuotedString)[]) { + set args(value: (string | vscode.ShellQuotedString)[] | undefined) { this._args = value || []; } diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index c9ecff2b0b9..3e389318075 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -8668,12 +8668,12 @@ declare module 'vscode' { /** * The shell command. Is `undefined` if created with a full command line. */ - command: string | ShellQuotedString; + command: string | ShellQuotedString | undefined; /** * The shell args. Is `undefined` if created with a full command line. */ - args: Array; + args: Array | undefined; /** * The shell options used when the command line is executed in a shell. From 23aee3d360c197e156556dd9a4984c813bd818dc Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 20 Dec 2024 12:28:50 -0600 Subject: [PATCH 448/479] Revert "Reland fix custom task shell doesn't work without manually passing in "run command" arg/flag" (#236726) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "Reland fix custom task shell doesn't work without manually passing in…" This reverts commit 330ab6c2928963dd83a7d90a271d9f2eb8db90d2. --- src/vs/platform/terminal/common/terminal.ts | 1 - .../tasks/browser/terminalTaskSystem.ts | 33 ++++++++----------- .../browser/terminalProfileResolverService.ts | 1 - 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 246ccaf6a76..97fc8e80462 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -872,7 +872,6 @@ export interface ITerminalProfile { overrideName?: boolean; color?: string; icon?: ThemeIcon | URI | { light: URI; dark: URI }; - isAutomationShell?: boolean; } export interface ITerminalDimensionsOverride extends Readonly { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 3b2c1e51c7f..e44eaa47cf9 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1112,6 +1112,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { color: task.configurationProperties.icon?.color || undefined, waitOnExit }; + let shellSpecified: boolean = false; const shellOptions: IShellConfiguration | undefined = task.command.options && task.command.options.shell; if (shellOptions) { if (shellOptions.executable) { @@ -1120,12 +1121,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig.args = undefined; } shellLaunchConfig.executable = await this._resolveVariable(variableResolver, shellOptions.executable); + shellSpecified = true; } if (shellOptions.args) { shellLaunchConfig.args = await this._resolveVariables(variableResolver, shellOptions.args.slice()); } } - const taskShellArgsSpecified = (defaultProfile.isAutomationShell ? shellLaunchConfig.args : shellOptions?.args) !== undefined; if (shellLaunchConfig.args === undefined) { shellLaunchConfig.args = []; } @@ -1138,34 +1139,29 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { windowsShellArgs = true; // If we don't have a cwd, then the terminal uses the home dir. const userHome = await this._pathService.userHome(); - if (basename === 'cmd.exe') { - if ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath))) { - return undefined; - } - if (!taskShellArgsSpecified) { - toAdd.push('/d', '/c'); - } - } else if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { - if (!taskShellArgsSpecified) { + if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { + return undefined; + } + if ((basename === 'powershell.exe') || (basename === 'pwsh.exe')) { + if (!shellSpecified) { toAdd.push('-Command'); } } else if ((basename === 'bash.exe') || (basename === 'zsh.exe')) { windowsShellArgs = false; - if (!taskShellArgsSpecified) { + if (!shellSpecified) { toAdd.push('-c'); } } else if (basename === 'wsl.exe') { - if (!taskShellArgsSpecified) { + if (!shellSpecified) { toAdd.push('-e'); } } else { - if (!taskShellArgsSpecified) { - // Push `-c` for unknown shells if the user didn't specify the args - toAdd.push('-c'); + if (!shellSpecified) { + toAdd.push('/d', '/c'); } } } else { - if (!taskShellArgsSpecified) { + if (!shellSpecified) { // Under Mac remove -l to not start it as a login shell. if (platform === Platform.Platform.Mac) { // Background on -l on osx https://github.com/microsoft/vscode/issues/107563 @@ -1272,12 +1268,11 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const combinedShellArgs: string[] = Objects.deepClone(configuredShellArgs); shellCommandArgs.forEach(element => { const shouldAddShellCommandArg = configuredShellArgs.every((arg, index) => { - const isDuplicated = arg.toLowerCase() === element.toLowerCase(); - if (isDuplicated && (configuredShellArgs.length > index + 1)) { + if ((arg.toLowerCase() === element) && (configuredShellArgs.length > index + 1)) { // We can still add the argument, but only if not all of the following arguments begin with "-". return !configuredShellArgs.slice(index + 1).every(testArg => testArg.startsWith('-')); } else { - return !isDuplicated; + return arg.toLowerCase() !== element; } }); if (shouldAddShellCommandArg) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts index e54a12a1b44..6208cfc0413 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProfileResolverService.ts @@ -268,7 +268,6 @@ export abstract class BaseTerminalProfileResolverService extends Disposable impl const automationProfile = this._configurationService.getValue(`terminal.integrated.automationProfile.${this._getOsKey(options.os)}`); if (this._isValidAutomationProfile(automationProfile, options.os)) { automationProfile.icon = this._getCustomIcon(automationProfile.icon) || Codicon.tools; - automationProfile.isAutomationShell = true; return automationProfile; } From 0f2ee1f669f1e3219be9f8764aafb34208f1f586 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 20 Dec 2024 10:29:11 -0800 Subject: [PATCH 449/479] fix: promote suggested file to working set when opened (#236737) --- .../contrib/chat/browser/chatEditing/chatEditingSession.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index 31e9a672a13..8552946891f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts @@ -265,8 +265,8 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } if (existingTransientEntries.has(uri)) { existingTransientEntries.delete(uri); - } else if (!this._workingSet.has(uri) && !this._removedTransientEntries.has(uri)) { - // Don't add as a transient entry if it's already part of the working set + } else if ((!this._workingSet.has(uri) || this._workingSet.get(uri)?.state === WorkingSetEntryState.Suggested) && !this._removedTransientEntries.has(uri)) { + // Don't add as a transient entry if it's already a confirmed part of the working set // or if the user has intentionally removed it from the working set activeEditors.add(uri); } From 720422ca070ad31eaafb8ab0e1b89a7d781f5e07 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 20 Dec 2024 10:29:24 -0800 Subject: [PATCH 450/479] chore: bump milestone in work notebook (#236730) --- .vscode/notebooks/my-work.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index b47a08b5a18..e8b184f8e57 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:\"November 2024\"\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:\"January 2025\"\n" }, { "kind": 1, From eee5e7643a2481ee1dac9a7e75f922ccc4e40f40 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 20 Dec 2024 10:29:40 -0800 Subject: [PATCH 451/479] cli: propagate server-data-dir and extensions-dir when installing service (#236734) Fixes #236195 --- cli/src/bin/code/main.rs | 4 +-- cli/src/commands/tunnels.rs | 55 ++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/cli/src/bin/code/main.rs b/cli/src/bin/code/main.rs index 0e4809b78c7..b73d0aa885b 100644 --- a/cli/src/bin/code/main.rs +++ b/cli/src/bin/code/main.rs @@ -103,7 +103,7 @@ async fn main() -> Result<(), std::convert::Infallible> { serve_web::serve_web(context!(), sw_args).await } - Some(args::Commands::Tunnel(tunnel_args)) => match tunnel_args.subcommand { + Some(args::Commands::Tunnel(mut tunnel_args)) => match tunnel_args.subcommand.take() { Some(args::TunnelSubcommand::Prune) => tunnels::prune(context!()).await, Some(args::TunnelSubcommand::Unregister) => tunnels::unregister(context!()).await, Some(args::TunnelSubcommand::Kill) => tunnels::kill(context!()).await, @@ -116,7 +116,7 @@ async fn main() -> Result<(), std::convert::Infallible> { tunnels::user(context!(), user_command).await } Some(args::TunnelSubcommand::Service(service_args)) => { - tunnels::service(context_no_logger(), service_args).await + tunnels::service(context_no_logger(), tunnel_args, service_args).await } Some(args::TunnelSubcommand::ForwardInternal(forward_args)) => { tunnels::forward(context_no_logger(), forward_args).await diff --git a/cli/src/commands/tunnels.rs b/cli/src/commands/tunnels.rs index 2a0b4c7ddce..f52fa714793 100644 --- a/cli/src/commands/tunnels.rs +++ b/cli/src/commands/tunnels.rs @@ -21,7 +21,7 @@ use tokio::{ use super::{ args::{ - AuthProvider, CliCore, CommandShellArgs, ExistingTunnelArgs, TunnelForwardArgs, + AuthProvider, CliCore, CommandShellArgs, ExistingTunnelArgs, TunnelArgs, TunnelForwardArgs, TunnelRenameArgs, TunnelServeArgs, TunnelServiceSubCommands, TunnelUserSubCommands, }, CommandContext, @@ -104,12 +104,16 @@ fn fulfill_existing_tunnel_args( } struct TunnelServiceContainer { - args: CliCore, + core_args: CliCore, + tunnel_args: TunnelArgs, } impl TunnelServiceContainer { - fn new(args: CliCore) -> Self { - Self { args } + fn new(core_args: CliCore, tunnel_args: TunnelArgs) -> Self { + Self { + core_args, + tunnel_args, + } } } @@ -120,7 +124,8 @@ impl ServiceContainer for TunnelServiceContainer { log: log::Logger, launcher_paths: LauncherPaths, ) -> Result<(), AnyError> { - let csa = (&self.args).into(); + let mut csa = (&self.core_args).into(); + self.tunnel_args.serve_args.server_args.apply_to(&mut csa); serve_with_csa( launcher_paths, log, @@ -242,8 +247,27 @@ async fn is_port_available(host: IpAddr, port: u16) -> bool { .is_ok() } +fn make_service_args<'a: 'c, 'b: 'c, 'c>( + root_path: &'a str, + tunnel_args: &'b TunnelArgs, +) -> Vec<&'c str> { + let mut args = ["--verbose", "--cli-data-dir", root_path, "tunnel"].to_vec(); + + if let Some(d) = tunnel_args.serve_args.server_args.extensions_dir.as_ref() { + args.extend_from_slice(&["--extensions-dir", d]); + } + if let Some(d) = tunnel_args.serve_args.server_args.server_data_dir.as_ref() { + args.extend_from_slice(&["--server-data-dir", d]); + } + + args.extend_from_slice(&["service", "internal-run"]); + + args +} + pub async fn service( ctx: CommandContext, + tunnel_args: TunnelArgs, service_args: TunnelServiceSubCommands, ) -> Result { let manager = create_service_manager(ctx.log.clone(), &ctx.paths); @@ -265,20 +289,10 @@ pub async fn service( legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; let current_exe = canonical_exe().map_err(|e| wrap(e, "could not get current exe"))?; + let root_path = ctx.paths.root().as_os_str().to_string_lossy(); + let args = make_service_args(&root_path, &tunnel_args); - manager - .register( - current_exe, - &[ - "--verbose", - "--cli-data-dir", - ctx.paths.root().as_os_str().to_string_lossy().as_ref(), - "tunnel", - "service", - "internal-run", - ], - ) - .await?; + manager.register(current_exe, &args).await?; ctx.log.result(format!("Service successfully installed! You can use `{APPLICATION_NAME} tunnel service log` to monitor it, and `{APPLICATION_NAME} tunnel service uninstall` to remove it.")); } TunnelServiceSubCommands::Uninstall => { @@ -289,7 +303,10 @@ pub async fn service( } TunnelServiceSubCommands::InternalRun => { manager - .run(ctx.paths.clone(), TunnelServiceContainer::new(ctx.args)) + .run( + ctx.paths.clone(), + TunnelServiceContainer::new(ctx.args, tunnel_args), + ) .await?; } } From 15da1932a92e1ba185931b57be5a07a9f2e1cd08 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 20 Dec 2024 12:36:07 -0600 Subject: [PATCH 452/479] Allow completions to be requested earlier (#236630) --- .../terminal/browser/terminalInstance.ts | 52 ++++++++++- .../browser/terminal.suggest.contribution.ts | 91 +++++++++---------- .../suggest/browser/terminalSuggestAddon.ts | 16 +++- 3 files changed, 107 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index df286dd974a..589b1deaf9c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -504,8 +504,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Resolve the executable ahead of time if shell integration is enabled, this should not // be done for custom PTYs as that would cause extension Pseudoterminal-based terminals // to hang in resolver extensions + let os: OperatingSystem | undefined; if (!this.shellLaunchConfig.customPtyImplementation && this._terminalConfigurationService.config.shellIntegration?.enabled && !this.shellLaunchConfig.executable) { - const os = await this._processManager.getBackendOS(); + os = await this._processManager.getBackendOS(); const defaultProfile = (await this._terminalProfileResolverService.getDefaultProfile({ remoteAuthority: this.remoteAuthority, os })); this.shellLaunchConfig.executable = defaultProfile.path; this.shellLaunchConfig.args = defaultProfile.args; @@ -521,6 +522,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } + // Resolve the shell type ahead of time to allow features that depend upon it to work + // before the process is actually created (like terminal suggest manual request) + if (os && this.shellLaunchConfig.executable) { + this.setShellType(guessShellTypeFromExecutable(os, this.shellLaunchConfig.executable)); + } + await this._createProcess(); // Re-establish the title after reconnect @@ -2656,3 +2663,46 @@ export class TerminalInstanceColorProvider implements IXtermColorProvider { return theme.getColor(SIDE_BAR_BACKGROUND); } } + +function guessShellTypeFromExecutable(os: OperatingSystem, executable: string): TerminalShellType | undefined { + const exeBasename = path.basename(executable); + const generalShellTypeMap: Map = new Map([ + [GeneralShellType.Julia, /^julia$/], + [GeneralShellType.NuShell, /^nu$/], + [GeneralShellType.PowerShell, /^pwsh(-preview)?|powershell$/], + [GeneralShellType.Python, /^py(?:thon)?$/] + ]); + for (const [shellType, pattern] of generalShellTypeMap) { + if (exeBasename.match(pattern)) { + return shellType; + } + } + + if (os === OperatingSystem.Windows) { + const windowsShellTypeMap: Map = new Map([ + [WindowsShellType.CommandPrompt, /^cmd$/], + [WindowsShellType.GitBash, /^bash$/], + [WindowsShellType.Wsl, /^wsl$/] + ]); + for (const [shellType, pattern] of windowsShellTypeMap) { + if (exeBasename.match(pattern)) { + return shellType; + } + } + } else { + const posixShellTypes: PosixShellType[] = [ + PosixShellType.Bash, + PosixShellType.Csh, + PosixShellType.Fish, + PosixShellType.Ksh, + PosixShellType.Sh, + PosixShellType.Zsh, + ]; + for (const type of posixShellTypes) { + if (exeBasename === type) { + return type; + } + } + } + return undefined; +} diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts index 35a845c1f7b..473de7f5f08 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts @@ -12,14 +12,13 @@ import { DisposableStore, MutableDisposable, toDisposable } from '../../../../.. import { isWindows } from '../../../../../base/common/platform.js'; import { localize2 } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; -import { ContextKeyExpr, IContextKey, IContextKeyService, IReadableSet } from '../../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { GeneralShellType, TerminalLocation, TerminalSettingId } from '../../../../../platform/terminal/common/terminal.js'; +import { GeneralShellType, TerminalLocation } from '../../../../../platform/terminal/common/terminal.js'; import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from '../../../terminal/browser/terminal.js'; import { registerActiveInstanceAction } from '../../../terminal/browser/terminalActions.js'; import { registerTerminalContribution, type ITerminalContributionContext } from '../../../terminal/browser/terminalExtensions.js'; -import { TERMINAL_CONFIG_SECTION, type ITerminalConfiguration } from '../../../terminal/common/terminal.js'; import { TerminalContextKeys } from '../../../terminal/common/terminalContextKey.js'; import { TerminalSuggestCommandId } from '../common/terminal.suggest.js'; import { terminalSuggestConfigSection, TerminalSuggestSettingId, type ITerminalSuggestConfiguration } from '../common/terminalSuggestConfiguration.js'; @@ -42,8 +41,7 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo private readonly _addon: MutableDisposable = new MutableDisposable(); private readonly _pwshAddon: MutableDisposable = new MutableDisposable(); - private _terminalSuggestWidgetContextKeys: IReadableSet = new Set(TerminalContextKeys.suggestWidgetVisible.key); - private _terminalSuggestWidgetVisibleContextKey: IContextKey; + private readonly _terminalSuggestWidgetVisibleContextKey: IContextKey; get addon(): SuggestAddon | undefined { return this._addon.value; } get pwshAddon(): PwshCompletionProviderAddon | undefined { return this._pwshAddon.value; } @@ -87,23 +85,15 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo if (!enabled) { return; } + this._loadAddons(xterm.raw); this.add(Event.runAndSubscribe(this._ctx.instance.onDidChangeShellType, async () => { - this._loadAddons(xterm.raw); - })); - this.add(this._contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(this._terminalSuggestWidgetContextKeys)) { - this._loadAddons(xterm.raw); - } - })); - this.add(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(TerminalSettingId.SendKeybindingsToShell)) { - this._loadAddons(xterm.raw); - } + this._refreshAddons(); })); } private _loadPwshCompletionAddon(xterm: RawXtermTerminal): void { if (this._ctx.instance.shellType !== GeneralShellType.PowerShell) { + this._pwshAddon.clear(); return; } const pwshCompletionProviderAddon = this._pwshAddon.value = this._instantiationService.createInstance(PwshCompletionProviderAddon, undefined, this._ctx.instance.capabilities); @@ -139,42 +129,47 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo } private _loadAddons(xterm: RawXtermTerminal): void { - const sendingKeybindingsToShell = this._configurationService.getValue(TERMINAL_CONFIG_SECTION).sendKeybindingsToShell; - if (sendingKeybindingsToShell || !this._ctx.instance.shellType) { - this._addon.clear(); - this._pwshAddon.clear(); + // Don't re-create the addon + if (this._addon.value) { return; } - if (this._terminalSuggestWidgetVisibleContextKey) { - const addon = this._addon.value = this._instantiationService.createInstance(SuggestAddon, this._ctx.instance.shellType, this._ctx.instance.capabilities, this._terminalSuggestWidgetVisibleContextKey); - xterm.loadAddon(addon); - this._loadPwshCompletionAddon(xterm); - if (this._ctx.instance.target === TerminalLocation.Editor) { - addon.setContainerWithOverflow(xterm.element!); - } else { - addon.setContainerWithOverflow(dom.findParentWithClass(xterm.element!, 'panel')!); - } - addon.setScreen(xterm.element!.querySelector('.xterm-screen')!); - this.add(this._ctx.instance.onDidBlur(() => addon.hideSuggestWidget())); - this.add(addon.onAcceptedCompletion(async text => { - this._ctx.instance.focus(); - this._ctx.instance.sendText(text, false); - })); - const clipboardContrib = TerminalClipboardContribution.get(this._ctx.instance)!; - this.add(clipboardContrib.onWillPaste(() => addon.isPasting = true)); - this.add(clipboardContrib.onDidPaste(() => { - // Delay this slightly as synchronizing the prompt input is debounced - setTimeout(() => addon.isPasting = false, 100); + + const addon = this._addon.value = this._instantiationService.createInstance(SuggestAddon, this._ctx.instance.shellType, this._ctx.instance.capabilities, this._terminalSuggestWidgetVisibleContextKey); + xterm.loadAddon(addon); + this._loadPwshCompletionAddon(xterm); + if (this._ctx.instance.target === TerminalLocation.Editor) { + addon.setContainerWithOverflow(xterm.element!); + } else { + addon.setContainerWithOverflow(dom.findParentWithClass(xterm.element!, 'panel')!); + } + addon.setScreen(xterm.element!.querySelector('.xterm-screen')!); + this.add(this._ctx.instance.onDidBlur(() => addon.hideSuggestWidget())); + this.add(addon.onAcceptedCompletion(async text => { + this._ctx.instance.focus(); + this._ctx.instance.sendText(text, false); + })); + const clipboardContrib = TerminalClipboardContribution.get(this._ctx.instance)!; + this.add(clipboardContrib.onWillPaste(() => addon.isPasting = true)); + this.add(clipboardContrib.onDidPaste(() => { + // Delay this slightly as synchronizing the prompt input is debounced + setTimeout(() => addon.isPasting = false, 100); + })); + if (!isWindows) { + let barrier: AutoOpenBarrier | undefined; + this.add(addon.onDidReceiveCompletions(() => { + barrier?.open(); + barrier = undefined; })); - if (!isWindows) { - let barrier: AutoOpenBarrier | undefined; - this.add(addon.onDidReceiveCompletions(() => { - barrier?.open(); - barrier = undefined; - })); - } } } + + private _refreshAddons(): void { + const addon = this._addon.value; + if (!addon) { + return; + } + addon.shellType = this._ctx.instance.shellType; + } } registerTerminalContribution(TerminalSuggestContribution.ID, TerminalSuggestContribution); @@ -190,7 +185,7 @@ registerActiveInstanceAction({ primary: KeyMod.CtrlCmd | KeyCode.Space, mac: { primary: KeyMod.WinCtrl | KeyCode.Space }, weight: KeybindingWeight.WorkbenchContrib + 1, - when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.terminalShellIntegrationEnabled, ContextKeyExpr.equals(`config.${TerminalSuggestSettingId.Enabled}`, true)) + when: ContextKeyExpr.and(TerminalContextKeys.focus, ContextKeyExpr.equals(`config.${TerminalSuggestSettingId.Enabled}`, true)) }, run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.requestCompletions(true) }); diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index 94650c7525c..f750d76f089 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -73,6 +73,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest private _cancellationTokenSource: CancellationTokenSource | undefined; isPasting: boolean = false; + shellType: TerminalShellType | undefined; private readonly _onBell = this._register(new Emitter()); readonly onBell = this._onBell.event; @@ -89,8 +90,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest [TerminalCompletionItemKind.Argument, Codicon.symbolVariable] ]); + private _shouldSyncWhenReady: boolean = false; + constructor( - private readonly _shellType: TerminalShellType | undefined, + shellType: TerminalShellType | undefined, private readonly _capabilities: ITerminalCapabilityStore, private readonly _terminalSuggestWidgetVisibleContextKey: IContextKey, @ITerminalCompletionService private readonly _terminalCompletionService: ITerminalCompletionService, @@ -101,6 +104,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest ) { super(); + this.shellType = shellType; + this._register(Event.runAndSubscribe(Event.any( this._capabilities.onDidAddCapabilityType, this._capabilities.onDidRemoveCapabilityType @@ -113,6 +118,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._promptInputModel.onDidChangeInput(e => this._sync(e)), this._promptInputModel.onDidFinishInput(() => this.hideSuggestWidget()), ); + if (this._shouldSyncWhenReady) { + this._sync(this._promptInputModel); + this._shouldSyncWhenReady = false; + } } this._register(commandDetection.onCommandExecuted(() => this.hideSuggestWidget())); } else { @@ -140,7 +149,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest return; } - if (!this._shellType) { + if (!this.shellType) { return; } @@ -156,7 +165,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest await this._extensionService.activateByEvent('onTerminalCompletionsRequested'); } - const providedCompletions = await this._terminalCompletionService.provideCompletions(this._promptInputModel.prefix, this._promptInputModel.cursorIndex, this._shellType, token, doNotRequestExtensionCompletions); + const providedCompletions = await this._terminalCompletionService.provideCompletions(this._promptInputModel.prefix, this._promptInputModel.cursorIndex, this.shellType, token, doNotRequestExtensionCompletions); if (!providedCompletions?.length || token.isCancellationRequested) { return; } @@ -237,6 +246,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest async requestCompletions(explicitlyInvoked?: boolean): Promise { if (!this._promptInputModel) { + this._shouldSyncWhenReady = true; return; } From 7cf9dbecf611fbc38c006439e704ccc807fa5c0f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 20 Dec 2024 10:52:07 -0800 Subject: [PATCH 453/479] Fix escaping of raw values that contain `&` in md preview Fixes #236660 --- extensions/markdown-language-features/src/util/dom.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/util/dom.ts b/extensions/markdown-language-features/src/util/dom.ts index 0f6c00da9da..8bbce79c303 100644 --- a/extensions/markdown-language-features/src/util/dom.ts +++ b/extensions/markdown-language-features/src/util/dom.ts @@ -5,7 +5,10 @@ import * as vscode from 'vscode'; export function escapeAttribute(value: string | vscode.Uri): string { - return value.toString().replace(/"/g, '"'); + return value.toString() + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, '''); } export function getNonce() { From 094e96a2eacd2b0a14af7884c2988b00261f8505 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 20 Dec 2024 19:55:45 +0100 Subject: [PATCH 454/479] Introduces IObservableWithChange so that typescript does not show the default type for TChange in hovers. (#236629) * Introduces IObservableWithChange so that typescript does not show the default type for TChange in hovers. This should make it easier to understand types when observables (potentially nested) are involved. * Fixes monaco editor --- build/monaco/monaco.usage.recipe | 2 +- src/vs/base/common/event.ts | 16 +++++----- .../base/common/observableInternal/autorun.ts | 4 +-- src/vs/base/common/observableInternal/base.ts | 29 ++++++++++++------- .../base/common/observableInternal/derived.ts | 8 ++--- .../base/common/observableInternal/index.ts | 2 +- .../base/common/observableInternal/logging.ts | 8 ++--- .../base/common/observableInternal/utils.ts | 16 +++++----- src/vs/base/test/common/observable.test.ts | 10 +++---- src/vs/editor/browser/observableCodeEditor.ts | 6 ++-- .../diffEditor/components/diffEditorSash.ts | 2 +- .../widget/diffEditor/diffEditorOptions.ts | 4 +-- .../diffEditor/features/gutterFeature.ts | 2 +- .../editor/browser/widget/diffEditor/utils.ts | 4 +-- .../browser/model/inlineCompletionsModel.ts | 4 +-- .../widget/observableCodeEditor.test.ts | 4 +-- .../browser/model/textModelDiffs.ts | 6 ++-- .../chatEdit/notebookChatActionsOverlay.ts | 6 ++-- .../contrib/testing/common/observableUtils.ts | 6 ++-- 19 files changed, 73 insertions(+), 66 deletions(-) diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index e3c8cdd0916..a3369eb25a7 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -35,6 +35,6 @@ import * as editorAPI from './vs/editor/editor.api'; a = editorAPI.editor; a = editorAPI.languages; - const o: IObservable = null!; + const o: IObservable = null!; o.TChange; })(); diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 82921d00dac..d49f2ef276f 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -9,7 +9,7 @@ import { onUnexpectedError } from './errors.js'; import { createSingleCallFunction } from './functional.js'; import { combinedDisposable, Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from './lifecycle.js'; import { LinkedList } from './linkedList.js'; -import { IObservable, IObserver } from './observable.js'; +import { IObservable, IObservableWithChange, IObserver } from './observable.js'; import { StopWatch } from './stopwatch.js'; import { MicrotaskDelay } from './symbols.js'; @@ -666,7 +666,7 @@ export namespace Event { private _counter = 0; private _hasChanged = false; - constructor(readonly _observable: IObservable, store: DisposableStore | undefined) { + constructor(readonly _observable: IObservable, store: DisposableStore | undefined) { const options: EmitterOptions = { onWillAddFirstListener: () => { _observable.addObserver(this); @@ -687,21 +687,21 @@ export namespace Event { } } - beginUpdate(_observable: IObservable): void { + beginUpdate(_observable: IObservable): void { // assert(_observable === this.obs); this._counter++; } - handlePossibleChange(_observable: IObservable): void { + handlePossibleChange(_observable: IObservable): void { // assert(_observable === this.obs); } - handleChange(_observable: IObservable, _change: TChange): void { + handleChange(_observable: IObservableWithChange, _change: TChange): void { // assert(_observable === this.obs); this._hasChanged = true; } - endUpdate(_observable: IObservable): void { + endUpdate(_observable: IObservable): void { // assert(_observable === this.obs); this._counter--; if (this._counter === 0) { @@ -718,7 +718,7 @@ export namespace Event { * Creates an event emitter that is fired when the observable changes. * Each listeners subscribes to the emitter. */ - export function fromObservable(obs: IObservable, store?: DisposableStore): Event { + export function fromObservable(obs: IObservable, store?: DisposableStore): Event { const observer = new EmitterObserver(obs, store); return observer.emitter.event; } @@ -726,7 +726,7 @@ export namespace Event { /** * Each listener is attached to the observable directly. */ - export function fromObservableLight(observable: IObservable): Event { + export function fromObservableLight(observable: IObservable): Event { return (listener, thisArgs, disposables) => { let count = 0; let didChange = false; diff --git a/src/vs/base/common/observableInternal/autorun.ts b/src/vs/base/common/observableInternal/autorun.ts index b8a62629937..d2425e11012 100644 --- a/src/vs/base/common/observableInternal/autorun.ts +++ b/src/vs/base/common/observableInternal/autorun.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChangeContext, IObservable, IObserver, IReader } from './base.js'; +import { IChangeContext, IObservable, IObservableWithChange, IObserver, IReader } from './base.js'; import { DebugNameData, IDebugNameData } from './debugName.js'; import { assertFn, BugIndicatingError, DisposableStore, IDisposable, markAsDisposed, onBugIndicatingError, toDisposable, trackDisposable } from './commonFacade/deps.js'; import { getLogger } from './logging.js'; @@ -286,7 +286,7 @@ export class AutorunObserver implements IObserver, IReader } } - public handleChange(observable: IObservable, change: TChange): void { + public handleChange(observable: IObservableWithChange, change: TChange): void { if (this.dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) { try { const shouldReact = this._handleChange ? this._handleChange({ diff --git a/src/vs/base/common/observableInternal/base.ts b/src/vs/base/common/observableInternal/base.ts index c28e773b268..f2aa0466f78 100644 --- a/src/vs/base/common/observableInternal/base.ts +++ b/src/vs/base/common/observableInternal/base.ts @@ -9,6 +9,13 @@ import type { derivedOpts } from './derived.js'; import { getLogger, logObservable } from './logging.js'; import { keepObserved, recomputeInitiallyAndOnChange } from './utils.js'; +/** + * Represents an observable value. + * + * @template T The type of the values the observable can hold. + */ +export interface IObservable extends IObservableWithChange { } + /** * Represents an observable value. * @@ -18,7 +25,7 @@ import { keepObserved, recomputeInitiallyAndOnChange } from './utils.js'; * While observers can miss temporary values of an observable, * they will receive all change values (as long as they are subscribed)! */ -export interface IObservable { +export interface IObservableWithChange { /** * Returns the current value. * @@ -71,7 +78,7 @@ export interface IObservable { * ONLY FOR DEBUGGING! * Logs computations of this derived. */ - log(): IObservable; + log(): IObservableWithChange; /** * Makes sure this value is computed eagerly. @@ -98,7 +105,7 @@ export interface IReader { /** * Reads the value of an observable and subscribes to it. */ - readObservable(observable: IObservable): T; + readObservable(observable: IObservableWithChange): T; } /** @@ -143,7 +150,7 @@ export interface IObserver { * * @param change Indicates how or why the value changed. */ - handleChange(observable: IObservable, change: TChange): void; + handleChange(observable: IObservableWithChange, change: TChange): void; } export interface ISettable { @@ -162,7 +169,7 @@ export interface ITransaction { * Calls {@link Observer.beginUpdate} immediately * and {@link Observer.endUpdate} when the transaction ends. */ - updateObserver(observer: IObserver, observable: IObservable): void; + updateObserver(observer: IObserver, observable: IObservableWithChange): void; } let _recomputeInitiallyAndOnChange: typeof recomputeInitiallyAndOnChange; @@ -185,7 +192,7 @@ export function _setDerivedOpts(derived: typeof _derived) { _derived = derived; } -export abstract class ConvenientObservable implements IObservable { +export abstract class ConvenientObservable implements IObservableWithChange { get TChange(): TChange { return null!; } public abstract get(): T; @@ -239,7 +246,7 @@ export abstract class ConvenientObservable implements IObservable { + public log(): IObservableWithChange { logObservable(this); return this; } @@ -248,7 +255,7 @@ export abstract class ConvenientObservable implements IObservable(this: IObservable>): IObservable { + public flatten(this: IObservable>): IObservable { return _derived( { owner: undefined, @@ -390,7 +397,7 @@ export class TransactionImpl implements ITransaction { /** * A settable observable. */ -export interface ISettableObservable extends IObservable, ISettable { +export interface ISettableObservable extends IObservableWithChange, ISettable { } /** @@ -505,11 +512,11 @@ export interface IChangeTracker { } export interface IChangeContext { - readonly changedObservable: IObservable; + readonly changedObservable: IObservableWithChange; readonly change: unknown; /** * Returns if the given observable caused the change. */ - didChange(observable: IObservable): this is { change: TChange }; + didChange(observable: IObservableWithChange): this is { change: TChange }; } diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index ba018041799..5421b023df9 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BaseObservable, IChangeContext, IObservable, IObserver, IReader, ISettableObservable, ITransaction, _setDerivedOpts, } from './base.js'; +import { BaseObservable, IChangeContext, IObservable, IObservableWithChange, IObserver, IReader, ISettableObservable, ITransaction, _setDerivedOpts, } from './base.js'; import { DebugNameData, DebugOwner, IDebugNameData } from './debugName.js'; import { BugIndicatingError, DisposableStore, EqualityComparer, IDisposable, assertFn, onBugIndicatingError, strictEquals } from './commonFacade/deps.js'; import { getLogger } from './logging.js'; @@ -386,7 +386,7 @@ export class Derived extends BaseObservable im assertFn(() => this.updateCount >= 0); } - public handlePossibleChange(observable: IObservable): void { + public handlePossibleChange(observable: IObservable): void { // In all other states, observers already know that we might have changed. if (this.state === DerivedState.upToDate && this.dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) { this.state = DerivedState.dependenciesMightHaveChanged; @@ -396,7 +396,7 @@ export class Derived extends BaseObservable im } } - public handleChange(observable: IObservable, change: TChange): void { + public handleChange(observable: IObservableWithChange, change: TChange): void { if (this.dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) { let shouldReact = false; try { @@ -460,7 +460,7 @@ export class Derived extends BaseObservable im super.removeObserver(observer); } - public override log(): IObservable { + public override log(): IObservableWithChange { if (!getLogger()) { super.log(); getLogger()?.handleDerivedCreated(this); diff --git a/src/vs/base/common/observableInternal/index.ts b/src/vs/base/common/observableInternal/index.ts index 9295f2697d5..873cf946171 100644 --- a/src/vs/base/common/observableInternal/index.ts +++ b/src/vs/base/common/observableInternal/index.ts @@ -7,7 +7,7 @@ export { observableValueOpts } from './api.js'; export { autorun, autorunDelta, autorunHandleChanges, autorunOpts, autorunWithStore, autorunWithStoreHandleChanges } from './autorun.js'; -export { asyncTransaction, disposableObservableValue, globalTransaction, observableValue, subtransaction, transaction, TransactionImpl, type IChangeContext, type IChangeTracker, type IObservable, type IObserver, type IReader, type ISettable, type ISettableObservable, type ITransaction, } from './base.js'; +export { asyncTransaction, disposableObservableValue, globalTransaction, observableValue, subtransaction, transaction, TransactionImpl, type IChangeContext, type IChangeTracker, type IObservable, type IObservableWithChange, type IObserver, type IReader, type ISettable, type ISettableObservable, type ITransaction, } from './base.js'; export { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter, derivedWithStore } from './derived.js'; export { ObservableLazy, ObservableLazyPromise, ObservablePromise, PromiseResult, } from './promise.js'; export { derivedWithCancellationToken, waitForState } from './utilsCancellation.js'; diff --git a/src/vs/base/common/observableInternal/logging.ts b/src/vs/base/common/observableInternal/logging.ts index a6a5bb78a06..8dc526a45be 100644 --- a/src/vs/base/common/observableInternal/logging.ts +++ b/src/vs/base/common/observableInternal/logging.ts @@ -39,7 +39,7 @@ interface IChangeInformation { } export interface IObservableLogger { - handleObservableChanged(observable: IObservable, info: IChangeInformation): void; + handleObservableChanged(observable: IObservable, info: IChangeInformation): void; handleFromEventObservableTriggered(observable: FromEventObservable, info: IChangeInformation): void; handleAutorunCreated(autorun: AutorunObserver): void; @@ -101,7 +101,7 @@ export class ConsoleObservableLogger implements IObservableLogger { : [normalText(` (unchanged)`)]; } - handleObservableChanged(observable: IObservable, info: IChangeInformation): void { + handleObservableChanged(observable: IObservable, info: IChangeInformation): void { if (!this._isIncluded(observable)) { return; } console.log(...this.textToConsoleArgs([ formatKind('observable value changed'), @@ -110,9 +110,9 @@ export class ConsoleObservableLogger implements IObservableLogger { ])); } - private readonly changedObservablesSets = new WeakMap>>(); + private readonly changedObservablesSets = new WeakMap>>(); - formatChanges(changes: Set>): ConsoleText | undefined { + formatChanges(changes: Set>): ConsoleText | undefined { if (changes.size === 0) { return undefined; } diff --git a/src/vs/base/common/observableInternal/utils.ts b/src/vs/base/common/observableInternal/utils.ts index 2fb57d1a42a..c42f12f7b8e 100644 --- a/src/vs/base/common/observableInternal/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { autorun, autorunOpts, autorunWithStoreHandleChanges } from './autorun.js'; -import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, _setKeepObserved, _setRecomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from './base.js'; +import { BaseObservable, ConvenientObservable, IObservable, IObservableWithChange, IObserver, IReader, ITransaction, _setKeepObserved, _setRecomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from './base.js'; import { DebugNameData, DebugOwner, IDebugNameData, getDebugName, } from './debugName.js'; import { BugIndicatingError, DisposableStore, EqualityComparer, Event, IDisposable, IValueWithChangeEvent, strictEquals, toDisposable } from './commonFacade/deps.js'; import { derived, derivedOpts } from './derived.js'; @@ -259,7 +259,7 @@ export function observableSignal(debugNameOrOwner: string | objec } } -export interface IObservableSignal extends IObservable { +export interface IObservableSignal extends IObservableWithChange { trigger(tx: ITransaction | undefined, change: TChange): void; } @@ -434,11 +434,11 @@ export class KeepAliveObserver implements IObserver { private readonly _handleValue: ((value: any) => void) | undefined, ) { } - beginUpdate(observable: IObservable): void { + beginUpdate(observable: IObservable): void { this._counter++; } - endUpdate(observable: IObservable): void { + endUpdate(observable: IObservable): void { this._counter--; if (this._counter === 0 && this._forceRecompute) { if (this._handleValue) { @@ -449,11 +449,11 @@ export class KeepAliveObserver implements IObserver { } } - handlePossibleChange(observable: IObservable): void { + handlePossibleChange(observable: IObservable): void { // NO OP } - handleChange(observable: IObservable, change: TChange): void { + handleChange(observable: IObservableWithChange, change: TChange): void { // NO OP } } @@ -625,7 +625,7 @@ export function derivedConstOnceDefined(owner: DebugOwner, fn: (reader: IRead type RemoveUndefined = T extends undefined ? never : T; -export function runOnChange(observable: IObservable, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined[]) => void): IDisposable { +export function runOnChange(observable: IObservableWithChange, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined[]) => void): IDisposable { let _previousValue: T | undefined; return autorunWithStoreHandleChanges({ createEmptyChangeSummary: () => ({ deltas: [] as RemoveUndefined[], didChange: false }), @@ -649,7 +649,7 @@ export function runOnChange(observable: IObservable, cb: }); } -export function runOnChangeWithStore(observable: IObservable, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined[], store: DisposableStore) => void): IDisposable { +export function runOnChangeWithStore(observable: IObservableWithChange, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined[], store: DisposableStore) => void): IDisposable { const store = new DisposableStore(); const disposable = runOnChange(observable, (value, previousValue: undefined | T, deltas) => { store.clear(); diff --git a/src/vs/base/test/common/observable.test.ts b/src/vs/base/test/common/observable.test.ts index 3d7ed472fcd..5d33ac30a9c 100644 --- a/src/vs/base/test/common/observable.test.ts +++ b/src/vs/base/test/common/observable.test.ts @@ -8,7 +8,7 @@ import { setUnexpectedErrorHandler } from '../../common/errors.js'; import { Emitter, Event } from '../../common/event.js'; import { DisposableStore } from '../../common/lifecycle.js'; import { autorun, autorunHandleChanges, derived, derivedDisposable, IObservable, IObserver, ISettableObservable, ITransaction, keepObserved, observableFromEvent, observableSignal, observableValue, transaction, waitForState } from '../../common/observable.js'; -import { BaseObservable } from '../../common/observableInternal/base.js'; +import { BaseObservable, IObservableWithChange } from '../../common/observableInternal/base.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; suite('observables', () => { @@ -1486,18 +1486,18 @@ export class LoggingObserver implements IObserver { constructor(public readonly debugName: string, private readonly log: Log) { } - beginUpdate(observable: IObservable): void { + beginUpdate(observable: IObservable): void { this.count++; this.log.log(`${this.debugName}.beginUpdate (count ${this.count})`); } - endUpdate(observable: IObservable): void { + endUpdate(observable: IObservable): void { this.log.log(`${this.debugName}.endUpdate (count ${this.count})`); this.count--; } - handleChange(observable: IObservable, change: TChange): void { + handleChange(observable: IObservableWithChange, change: TChange): void { this.log.log(`${this.debugName}.handleChange (count ${this.count})`); } - handlePossibleChange(observable: IObservable): void { + handlePossibleChange(observable: IObservable): void { this.log.log(`${this.debugName}.handlePossibleChange`); } } diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index 3633d4cac45..ac1ab182fa4 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -5,7 +5,7 @@ import { equalsIfDefined, itemsEquals } from '../../base/common/equals.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js'; -import { IObservable, ITransaction, TransactionImpl, autorun, autorunOpts, derived, derivedOpts, derivedWithSetter, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.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 { OffsetRange } from '../common/core/offsetRange.js'; @@ -155,13 +155,13 @@ export class ObservableCodeEditor extends Disposable { public readonly isReadonly = observableFromEvent(this, this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.readOnly)); private readonly _versionId = observableValueOpts({ owner: this, lazy: true }, this.editor.getModel()?.getVersionId() ?? null); - public readonly versionId: IObservable = this._versionId; + public readonly versionId: IObservableWithChange = this._versionId; private readonly _selections = observableValueOpts( { owner: this, equalsFn: equalsIfDefined(itemsEquals(Selection.selectionsEqual)), lazy: true }, this.editor.getSelections() ?? null ); - public readonly selections: IObservable = this._selections; + public readonly selections: IObservableWithChange = this._selections; public readonly positions = derivedOpts( diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts index 8abaddcceb9..468e6323c7f 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts @@ -62,7 +62,7 @@ export class DiffEditorSash extends Disposable { private readonly _domNode: HTMLElement, private readonly _dimensions: { height: IObservable; width: IObservable }, private readonly _enabled: IObservable, - private readonly _boundarySashes: IObservable, + private readonly _boundarySashes: IObservable, public readonly sashLeft: ISettableObservable, private readonly _resetSash: () => void, ) { diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts index 0c1a533681f..3d2e483ca15 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IObservable, ISettableObservable, derived, derivedConstOnceDefined, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; +import { IObservable, IObservableWithChange, ISettableObservable, derived, derivedConstOnceDefined, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { Constants } from '../../../../base/common/uint.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { diffEditorDefaultOptions } from '../../../common/config/diffEditor.js'; @@ -15,7 +15,7 @@ import { DiffEditorViewModel, DiffState } from './diffEditorViewModel.js'; export class DiffEditorOptions { private readonly _options: ISettableObservable, { changedOptions: IDiffEditorOptions }>; - public get editorOptions(): IObservable { return this._options; } + public get editorOptions(): IObservableWithChange { return this._options; } private readonly _diffEditorWidth = observableValue(this, 0); diff --git a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts index 556378aebf3..17ff41896b4 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts @@ -50,7 +50,7 @@ export class DiffEditorGutter extends Disposable { private readonly _editors: DiffEditorEditors, private readonly _options: DiffEditorOptions, private readonly _sashLayout: SashLayout, - private readonly _boundarySashes: IObservable, + private readonly _boundarySashes: IObservable, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IMenuService private readonly _menuService: IMenuService, diff --git a/src/vs/editor/browser/widget/diffEditor/utils.ts b/src/vs/editor/browser/widget/diffEditor/utils.ts index d34d60fd457..47faca2f8cf 100644 --- a/src/vs/editor/browser/widget/diffEditor/utils.ts +++ b/src/vs/editor/browser/widget/diffEditor/utils.ts @@ -7,7 +7,7 @@ import { IDimension } from '../../../../base/browser/dom.js'; import { findLast } from '../../../../base/common/arraysFind.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, autorunWithStore, observableValue, transaction } from '../../../../base/common/observable.js'; +import { IObservable, IObservableWithChange, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, autorunWithStore, observableValue, transaction } from '../../../../base/common/observable.js'; import { ElementSizeObserver } from '../../config/elementSizeObserver.js'; import { ICodeEditor, IOverlayWidget, IViewZone } from '../../editorBrowser.js'; import { Position } from '../../../common/core/position.js'; @@ -126,7 +126,7 @@ export class ObservableElementSizeObserver extends Disposable { } } -export function animatedObservable(targetWindow: Window, base: IObservable, store: DisposableStore): IObservable { +export function animatedObservable(targetWindow: Window, base: IObservableWithChange, store: DisposableStore): IObservable { let targetVal = base.get(); let startVal = targetVal; let curVal = targetVal; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index a15005e75fa..b77a2c0df81 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -7,7 +7,7 @@ import { mapFindFirst } from '../../../../../base/common/arraysFind.js'; import { itemsEquals } from '../../../../../base/common/equals.js'; import { BugIndicatingError, onUnexpectedError, onUnexpectedExternalError } from '../../../../../base/common/errors.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { IObservable, IReader, ITransaction, autorun, derived, derivedHandleChanges, derivedOpts, observableSignal, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from '../../../../../base/common/observable.js'; +import { IObservable, IObservableWithChange, IReader, ITransaction, autorun, derived, derivedHandleChanges, derivedOpts, observableSignal, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from '../../../../../base/common/observable.js'; import { commonPrefixLength, firstNonWhitespaceIndex } from '../../../../../base/common/strings.js'; import { isDefined } from '../../../../../base/common/types.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; @@ -63,7 +63,7 @@ export class InlineCompletionsModel extends Disposable { constructor( public readonly textModel: ITextModel, private readonly _selectedSuggestItem: IObservable, - public readonly _textModelVersionId: IObservable, + public readonly _textModelVersionId: IObservableWithChange, private readonly _positions: IObservable, private readonly _debounceValue: IFeatureDebounceInformation, private readonly _enabled: IObservable, diff --git a/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts b/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts index d9144896afe..e8406d868f8 100644 --- a/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts +++ b/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts @@ -112,7 +112,7 @@ suite("CodeEditorWidget", () => { })); test("listener interaction (unforced)", () => { - let derived: IObservable; + let derived: IObservable; let log: Log; withEditorSetupTestFixture( (editor, disposables) => { @@ -143,7 +143,7 @@ suite("CodeEditorWidget", () => { }); test("listener interaction ()", () => { - let derived: IObservable; + let derived: IObservable; let log: Log; withEditorSetupTestFixture( (editor, disposables) => { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts index 63512ae0e38..2741b6681d4 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts @@ -12,7 +12,7 @@ import { LineRangeEdit } from './editing.js'; import { LineRange } from './lineRange.js'; import { ReentrancyBarrier } from '../../../../../base/common/controlFlow.js'; import { IMergeDiffComputer } from './diffComputer.js'; -import { autorun, IObservable, IReader, ITransaction, observableSignal, observableValue, transaction } from '../../../../../base/common/observable.js'; +import { autorun, IObservableWithChange, IReader, ITransaction, observableSignal, observableValue, transaction } from '../../../../../base/common/observable.js'; import { UndoRedoGroup } from '../../../../../platform/undoRedo/common/undoRedo.js'; export class TextModelDiffs extends Disposable { @@ -61,14 +61,14 @@ export class TextModelDiffs extends Disposable { })); } - public get state(): IObservable { + public get state(): IObservableWithChange { return this._state; } /** * Diffs from base to input. */ - public get diffs(): IObservable { + public get diffs(): IObservableWithChange { return this._diffs; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts index 552a657df4d..7ac61fbcb30 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts @@ -24,7 +24,7 @@ import { navigationBearingFakeActionId } from '../../../../chat/browser/chatEdit export class NotebookChatActionsOverlayController extends Disposable { constructor( private readonly notebookEditor: INotebookEditor, - cellDiffInfo: IObservable, + cellDiffInfo: IObservable, deletedCellDecorator: INotebookDeletedCellDecorator, @IChatEditingService private readonly _chatEditingService: IChatEditingService, @IInstantiationService instantiationService: IInstantiationService, @@ -60,7 +60,7 @@ export class NotebookChatActionsOverlay extends Disposable { constructor( private readonly notebookEditor: INotebookEditor, entry: IModifiedFileEntry, - cellDiffInfo: IObservable, + cellDiffInfo: IObservable, nextEntry: IModifiedFileEntry, previousEntry: IModifiedFileEntry, deletedCellDecorator: INotebookDeletedCellDecorator, @@ -196,7 +196,7 @@ export class NotebookChatActionsOverlay extends Disposable { class NextPreviousChangeActionRunner extends ActionRunner { constructor( private readonly notebookEditor: INotebookEditor, - private readonly cellDiffInfo: IObservable, + private readonly cellDiffInfo: IObservable, private readonly entry: IModifiedFileEntry, private readonly next: IModifiedFileEntry, private readonly direction: 'next' | 'previous', diff --git a/src/vs/workbench/contrib/testing/common/observableUtils.ts b/src/vs/workbench/contrib/testing/common/observableUtils.ts index 3153f2e5bd8..9294c905002 100644 --- a/src/vs/workbench/contrib/testing/common/observableUtils.ts +++ b/src/vs/workbench/contrib/testing/common/observableUtils.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, IObserver } from '../../../../base/common/observable.js'; +import { IObservableWithChange, IObserver } from '../../../../base/common/observable.js'; -export function onObservableChange(observable: IObservable, callback: (value: T) => void): IDisposable { +export function onObservableChange(observable: IObservableWithChange, callback: (value: T) => void): IDisposable { const o: IObserver = { beginUpdate() { }, endUpdate() { }, handlePossibleChange(observable) { observable.reportChanges(); }, - handleChange(_observable: IObservable, change: TChange) { + handleChange(_observable: IObservableWithChange, change: TChange) { callback(change as any as T); } }; From 83c336d7d606a8d2c6f50c403a7fecc770e20cbe Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 20 Dec 2024 20:01:59 +0100 Subject: [PATCH 455/479] Log when a derived gets cleared (#236739) --- src/vs/base/common/observableInternal/derived.ts | 1 + src/vs/base/common/observableInternal/logging.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index 5421b023df9..54a1e99296d 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -220,6 +220,7 @@ export class Derived extends BaseObservable im */ this.state = DerivedState.initial; this.value = undefined; + getLogger()?.handleDerivedCleared(this); for (const d of this.dependencies) { d.removeObserver(this); } diff --git a/src/vs/base/common/observableInternal/logging.ts b/src/vs/base/common/observableInternal/logging.ts index 8dc526a45be..f0bc82170d8 100644 --- a/src/vs/base/common/observableInternal/logging.ts +++ b/src/vs/base/common/observableInternal/logging.ts @@ -48,6 +48,7 @@ export interface IObservableLogger { handleDerivedCreated(observable: Derived): void; handleDerivedRecomputed(observable: Derived, info: IChangeInformation): void; + handleDerivedCleared(observable: Derived): void; handleBeginTransaction(transaction: TransactionImpl): void; handleEndTransaction(): void; @@ -170,6 +171,15 @@ export class ConsoleObservableLogger implements IObservableLogger { changedObservables.clear(); } + handleDerivedCleared(derived: Derived): void { + if (!this._isIncluded(derived)) { return; } + + console.log(...this.textToConsoleArgs([ + formatKind('derived cleared'), + styled(derived.debugName, { color: 'BlueViolet' }), + ])); + } + handleFromEventObservableTriggered(observable: FromEventObservable, info: IChangeInformation): void { if (!this._isIncluded(observable)) { return; } From b9f07f1e9478a470353503582e8f5192a84a65e9 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 20 Dec 2024 20:02:18 +0100 Subject: [PATCH 456/479] Adds color descriptions. (#236741) --- .../view/inlineEdits/gutterIndicatorView.ts | 40 +++++++++++++++---- .../browser/view/inlineEdits/indicatorView.ts | 9 ++--- .../view/inlineEdits/sideBySideDiff.ts | 18 ++++----- .../view/inlineEdits/wordReplacementView.ts | 4 +- 4 files changed, 47 insertions(+), 24 deletions(-) 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 739f12b4feb..a3d7dcddbc1 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -17,15 +17,39 @@ import { OffsetRange } from '../../../../../common/core/offsetRange.js'; import { StickyScrollController } from '../../../../stickyScroll/browser/stickyScrollController.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { mapOutFalsy, n, rectToProps } from './utils.js'; +import { localize } from '../../../../../../nls.js'; +export const inlineEditIndicatorPrimaryForeground = registerColor( + 'inlineEdit.gutterIndicator.primaryForeground', + buttonForeground, + localize('inlineEdit.gutterIndicator.primaryForeground', 'Foreground color for the primary inline edit gutter indicator.') +); +export const inlineEditIndicatorPrimaryBackground = registerColor( + 'inlineEdit.gutterIndicator.primaryBackground', + buttonBackground, + localize('inlineEdit.gutterIndicator.primaryBackground', 'Background color for the primary inline edit gutter indicator.') +); -export const inlineEditIndicatorPrimaryForeground = registerColor('inlineEdit.gutterIndicator.primaryForeground', buttonForeground, 'Foreground color for the primary inline edit gutter indicator.'); -export const inlineEditIndicatorPrimaryBackground = registerColor('inlineEdit.gutterIndicator.primaryBackground', buttonBackground, 'Background color for the primary inline edit gutter indicator.'); - -export const inlineEditIndicatorSecondaryForeground = registerColor('inlineEdit.gutterIndicator.secondaryForeground', buttonSecondaryForeground, 'Foreground color for the secondary inline edit gutter indicator.'); -export const inlineEditIndicatorSecondaryBackground = registerColor('inlineEdit.gutterIndicator.secondaryBackground', buttonSecondaryBackground, 'Background color for the secondary inline edit gutter indicator.'); +export const inlineEditIndicatorSecondaryForeground = registerColor( + 'inlineEdit.gutterIndicator.secondaryForeground', + buttonSecondaryForeground, + localize('inlineEdit.gutterIndicator.secondaryForeground', 'Foreground color for the secondary inline edit gutter indicator.') +); +export const inlineEditIndicatorSecondaryBackground = registerColor( + 'inlineEdit.gutterIndicator.secondaryBackground', + buttonSecondaryBackground, + localize('inlineEdit.gutterIndicator.secondaryBackground', 'Background color for the secondary inline edit gutter indicator.') +); -export const inlineEditIndicatorsuccessfulForeground = registerColor('inlineEdit.gutterIndicator.successfulForeground', buttonForeground, 'Foreground color for the successful inline edit gutter indicator.'); -export const inlineEditIndicatorsuccessfulBackground = registerColor('inlineEdit.gutterIndicator.successfulBackground', { light: '#2e825c', dark: '#2e825c', hcLight: '#2e825c', hcDark: '#2e825c' }, 'Background color for the successful inline edit gutter indicator.'); +export const inlineEditIndicatorsuccessfulForeground = registerColor( + 'inlineEdit.gutterIndicator.successfulForeground', + buttonForeground, + localize('inlineEdit.gutterIndicator.successfulForeground', 'Foreground color for the successful inline edit gutter indicator.') +); +export const inlineEditIndicatorsuccessfulBackground = registerColor( + 'inlineEdit.gutterIndicator.successfulBackground', + { light: '#2e825c', dark: '#2e825c', hcLight: '#2e825c', hcDark: '#2e825c' }, + localize('inlineEdit.gutterIndicator.successfulBackground', 'Background color for the successful inline edit gutter indicator.') +); export const inlineEditIndicatorBackground = registerColor( 'inlineEdit.gutterIndicator.background', @@ -35,7 +59,7 @@ export const inlineEditIndicatorBackground = registerColor( dark: transparent('tab.inactiveBackground', 0.5), light: '#5f5f5f18', }, - 'Background color for the inline edit gutter indicator.' + localize('inlineEdit.gutterIndicator.background', 'Background color for the inline edit gutter indicator.') ); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts index f5845663658..00b129bbbfb 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/indicatorView.ts @@ -13,16 +13,15 @@ import { registerColor } from '../../../../../../platform/theme/common/colorUtil import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { OffsetRange } from '../../../../../common/core/offsetRange.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import { localize } from '../../../../../../nls.js'; export interface IInlineEditsIndicatorState { editTop: number; showAlways: boolean; } - - -export const inlineEditIndicatorForeground = registerColor('inlineEdit.indicator.foreground', buttonForeground, ''); -export const inlineEditIndicatorBackground = registerColor('inlineEdit.indicator.background', buttonBackground, ''); -export const inlineEditIndicatorBorder = registerColor('inlineEdit.indicator.border', buttonSeparator, ''); +export const inlineEditIndicatorForeground = registerColor('inlineEdit.indicator.foreground', buttonForeground, localize('inlineEdit.indicator.foreground', 'Foreground color for the inline edit indicator.')); +export const inlineEditIndicatorBackground = registerColor('inlineEdit.indicator.background', buttonBackground, localize('inlineEdit.indicator.background', 'Background color for the inline edit indicator.')); +export const inlineEditIndicatorBorder = registerColor('inlineEdit.indicator.border', buttonSeparator, localize('inlineEdit.indicator.border', 'Border color for the inline edit indicator.')); export class InlineEditsIndicator extends Disposable { private readonly _indicator = h('div.inline-edits-view-indicator', { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 042dc653cf9..7198e4870e9 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -32,48 +32,48 @@ import { InlineCompletionContextKeys } from '../../controller/inlineCompletionCo import { CustomizedMenuWorkbenchToolBar } from '../../hintsWidget/inlineCompletionsHintsWidget.js'; import { PathBuilder, StatusBarViewItem, getOffsetForPos, mapOutFalsy, maxContentWidthInRange, n } from './utils.js'; import { InlineEditWithChanges } from './viewAndDiffProducer.js'; +import { localize } from '../../../../../../nls.js'; export const originalBackgroundColor = registerColor( 'inlineEdit.originalBackground', Color.transparent, - '', + localize('inlineEdit.originalBackground', 'Background color for the original text in inline edits.'), true ); export const modifiedBackgroundColor = registerColor( 'inlineEdit.modifiedBackground', Color.transparent, - '', + localize('inlineEdit.modifiedBackground', 'Background color for the modified text in inline edits.'), true ); export const originalChangedLineBackgroundColor = registerColor( 'inlineEdit.originalChangedLineBackground', Color.transparent, - '', + localize('inlineEdit.originalChangedLineBackground', 'Background color for the changed lines in the original text of inline edits.'), true ); export const originalChangedTextOverlayColor = registerColor( 'inlineEdit.originalChangedTextBackground', diffRemoved, - '', + localize('inlineEdit.originalChangedTextBackground', 'Overlay color for the changed text in the original text of inline edits.'), true ); export const modifiedChangedLineBackgroundColor = registerColor( 'inlineEdit.modifiedChangedLineBackground', Color.transparent, - '', + localize('inlineEdit.modifiedChangedLineBackground', 'Background color for the changed lines in the modified text of inline edits.'), true ); export const modifiedChangedTextOverlayColor = registerColor( 'inlineEdit.modifiedChangedTextBackground', diffInserted, - '', + localize('inlineEdit.modifiedChangedTextBackground', 'Overlay color for the changed text in the modified text of inline edits.'), true ); - export const originalBorder = registerColor( 'inlineEdit.originalBorder', { @@ -82,7 +82,7 @@ export const originalBorder = registerColor( hcDark: editorLineHighlightBorder, hcLight: editorLineHighlightBorder }, - '' + localize('inlineEdit.originalBorder', 'Border color for the original text in inline edits.') ); export const modifiedBorder = registerColor( @@ -93,7 +93,7 @@ export const modifiedBorder = registerColor( hcDark: editorLineHighlightBorder, hcLight: editorLineHighlightBorder }, - '' + localize('inlineEdit.modifiedBorder', 'Border color for the modified text in inline edits.') ); export class InlineEditsSideBySideDiff extends Disposable { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts index 6e6f213d38a..19f28075c33 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/wordReplacementView.ts @@ -19,7 +19,7 @@ import { ILanguageService } from '../../../../../common/languages/language.js'; import { LineTokens } from '../../../../../common/tokens/lineTokens.js'; import { TokenArray } from '../../../../../common/tokens/tokenArray.js'; import { mapOutFalsy, n, rectToProps } from './utils.js'; - +import { localize } from '../../../../../../nls.js'; export const transparentHoverBackground = registerColor( 'inlineEdit.wordReplacementView.background', { @@ -28,7 +28,7 @@ export const transparentHoverBackground = registerColor( hcLight: transparent(editorHoverStatusBarBackground, 0.1), hcDark: transparent(editorHoverStatusBarBackground, 0.1), }, - 'Background color for the inline edit word replacement view.' + localize('inlineEdit.wordReplacementView.background', 'Background color for the inline edit word replacement view.') ); export class WordReplacementView extends Disposable { From 5d5976d10cde46dabc31bf264f637b8f6da413a9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 20 Dec 2024 11:22:59 -0800 Subject: [PATCH 457/479] fix: macos external terminal not opening in darkwin remotes (#236426) Closes #236425 --- build/gulpfile.reh.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 4f00317173d..e0df76f1c8f 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -63,6 +63,9 @@ const serverResourceIncludes = [ 'out-build/vs/base/node/cpuUsage.sh', 'out-build/vs/base/node/ps.sh', + // External Terminal + 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', + // Terminal shell integration 'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration.ps1', 'out-build/vs/workbench/contrib/terminal/common/scripts/CodeTabExpansion.psm1', From 9ee30e50da91f818e60c1c3ccf156f64c097bff1 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Fri, 20 Dec 2024 14:05:31 -0600 Subject: [PATCH 458/479] fix terminal completion issues with `replacementIndex` (#236728) fix replacement index weirdness + more --- .../suggest/browser/terminalSuggestAddon.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index f750d76f089..52d7c1b8c37 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -64,7 +64,6 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest private _leadingLineContent?: string; private _cursorIndexDelta: number = 0; private _requestedCompletionsIndex: number = 0; - private _providerReplacementIndex: number = 0; private _lastUserData?: string; static lastAcceptedCompletionTimestamp: number = 0; @@ -171,11 +170,6 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest } this._onDidReceiveCompletions.fire(); - // ATM, the two providers calculate the same replacement index / prefix, so we can just take the first one - // TODO: figure out if we can add support for multiple replacement indices - const replacementIndices = [...new Set(providedCompletions.map(c => c.replacementIndex))]; - const replacementIndex = replacementIndices.length === 1 ? replacementIndices[0] : 0; - this._providerReplacementIndex = replacementIndex; this._requestedCompletionsIndex = this._promptInputModel.cursorIndex; this._currentPromptInputState = { @@ -186,7 +180,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest ghostTextIndex: this._promptInputModel.ghostTextIndex }; - this._leadingLineContent = this._currentPromptInputState.prefix.substring(replacementIndex, replacementIndex + this._promptInputModel.cursorIndex + this._cursorIndexDelta); + this._leadingLineContent = this._currentPromptInputState.prefix.substring(0, this._requestedCompletionsIndex + this._cursorIndexDelta); const completions = providedCompletions.flat(); if (!completions?.length) { @@ -339,7 +333,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest if (this._terminalSuggestWidgetVisibleContextKey.get()) { this._cursorIndexDelta = this._currentPromptInputState.cursorIndex - (this._requestedCompletionsIndex); - let normalizedLeadingLineContent = this._currentPromptInputState.value.substring(this._providerReplacementIndex, this._requestedCompletionsIndex + this._cursorIndexDelta); + let normalizedLeadingLineContent = this._currentPromptInputState.value.substring(0, this._requestedCompletionsIndex + this._cursorIndexDelta); if (this._isFilteringDirectories) { normalizedLeadingLineContent = normalizePathSeparator(normalizedLeadingLineContent, this._pathSeparator); } @@ -458,7 +452,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest // The replacement text is any text after the replacement index for the completions, this // includes any text that was there before the completions were requested and any text added // since to refine the completion. - const replacementText = currentPromptInputState.value.substring(suggestion.item.completion.replacementIndex ?? this._providerReplacementIndex, currentPromptInputState.cursorIndex); + const replacementText = currentPromptInputState.value.substring(suggestion.item.completion.replacementIndex, currentPromptInputState.cursorIndex); // Right side of replacement text in the same word let rightSideReplacementText = ''; From 3751af286f68efe9a7ef11315ed0bfb06b8b8c14 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 20 Dec 2024 22:25:17 +0100 Subject: [PATCH 459/479] Implements inline edit indicator hover (#236738) --- .../browser/view/inlineCompletionsView.ts | 2 +- .../view/inlineEdits/gutterIndicatorMenu.ts | 152 ++++++++++++ .../view/inlineEdits/gutterIndicatorView.ts | 83 ++++++- .../view/inlineEdits/sideBySideDiff.ts | 4 +- .../browser/view/inlineEdits/utils.ts | 229 +++++++++++------- .../browser/view/inlineEdits/view.css | 21 +- .../browser/view/inlineEdits/view.ts | 4 +- 7 files changed, 395 insertions(+), 100 deletions(-) create mode 100644 src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.ts diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts index baaf3c08205..fa303f3fc6e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts @@ -48,7 +48,7 @@ export class InlineCompletionsView extends Disposable { constructor( private readonly _editor: ICodeEditor, private readonly _model: IObservable, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.ts new file mode 100644 index 00000000000..95168505a40 --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorMenu.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 { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { KeybindingLabel, unthemedKeybindingLabelOptions } from '../../../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; +import { Codicon } from '../../../../../../base/common/codicons.js'; +import { ResolvedKeybinding } from '../../../../../../base/common/keybindings.js'; +import { IObservable, autorun, constObservable, derived, derivedWithStore, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; +import { OS } from '../../../../../../base/common/platform.js'; +import { ThemeIcon } from '../../../../../../base/common/themables.js'; +import { localize } from '../../../../../../nls.js'; +import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; +import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { IKeybindingService } from '../../../../../../platform/keybinding/common/keybinding.js'; +import { Command } from '../../../../../common/languages.js'; +import { AcceptInlineCompletion, HideInlineCompletion, JumpToNextInlineEdit } from '../../controller/commands.js'; +import { ChildNode, FirstFnArg, LiveElement, n } from './utils.js'; + +export class GutterIndicatorMenuContent { + constructor( + private readonly _selectionOverride: IObservable<'jump' | 'accept' | undefined>, + private readonly _close: (focusEditor: boolean) => void, + private readonly _extensionCommands: IObservable, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @ICommandService private readonly _commandService: ICommandService, + ) { + } + + public toDisposableLiveElement(): LiveElement { + return this._createHoverContent().toDisposableLiveElement(); + } + + private _createHoverContent() { + const activeElement = observableValue('active', undefined); + const activeElementOrDefault = derived(reader => this._selectionOverride.read(reader) ?? activeElement.read(reader)); + + const createOptionArgs = (options: { id: string; title: string; icon: ThemeIcon; commandId: string; commandArgs?: unknown[] }): FirstFnArg => { + return { + title: options.title, + icon: options.icon, + keybinding: this._getKeybinding(options.commandArgs ? undefined : options.commandId), + isActive: activeElementOrDefault.map(v => v === options.id), + onHoverChange: v => activeElement.set(v ? options.id : undefined, undefined), + onAction: () => { + this._close(true); + return this._commandService.executeCommand(options.commandId, ...(options.commandArgs ?? [])); + }, + }; + }; + + // TODO make this menu contributable! + return hoverContent([ + // TODO: make header dynamic, get from extension + header(localize('inlineEdit', "Inline Edit")), + option(createOptionArgs({ id: 'jump', title: localize('jump', "Jump"), icon: Codicon.arrowRight, commandId: new JumpToNextInlineEdit().id })), + option(createOptionArgs({ id: 'accept', title: localize('accept', "Accept"), icon: Codicon.check, commandId: new AcceptInlineCompletion().id })), + option(createOptionArgs({ id: 'reject', title: localize('reject', "Reject"), icon: Codicon.close, commandId: new HideInlineCompletion().id })), + separator(), + this._extensionCommands?.map(c => c && c.length > 0 ? [ + ...c.map(c => option(createOptionArgs({ id: c.id, title: c.title, icon: Codicon.symbolEvent, commandId: c.id, commandArgs: c.arguments }))), + separator() + ] : []), + option(createOptionArgs({ id: 'settings', title: localize('settings', "Settings"), icon: Codicon.gear, commandId: 'workbench.action.openSettings', commandArgs: ['inlineSuggest.edits'] })), + ]); + } + + private _getKeybinding(commandId: string | undefined) { + if (!commandId) { + return constObservable(undefined); + } + return observableFromEvent(this._contextKeyService.onDidChangeContext, () => this._keybindingService.lookupKeybinding(commandId, this._contextKeyService, true)); + } +} + +function hoverContent(content: ChildNode) { + return n.div({ + class: 'content', + style: { + margin: 4, + minWidth: 150, + } + }, content); +} + +function header(title: string) { + return n.div({ + class: 'header', + style: { + color: 'var(--vscode-descriptionForeground)', + fontSize: '12px', + fontWeight: '600', + padding: '0 10px', + lineHeight: 26, + } + }, [title]); +} + +function option(props: { + title: string; + icon: ThemeIcon; + keybinding: IObservable; + isActive?: IObservable; + onHoverChange?: (isHovered: boolean) => void; + onAction?: () => void; +}) { + return derivedWithStore((_reader, store) => n.div({ + class: ['monaco-menu-option', props.isActive?.map(v => v && 'active')], + onmouseenter: () => props.onHoverChange?.(true), + onmouseleave: () => props.onHoverChange?.(false), + onclick: props.onAction, + onkeydown: e => { + if (e.key === 'Enter') { + props.onAction?.(); + } + }, + tabIndex: 0, + }, [ + n.elem('span', { + style: { + fontSize: 16, + display: 'flex', + } + }, [renderIcon(props.icon)]), + n.elem('span', {}, [props.title]), + n.div({ + style: { marginLeft: 'auto', opacity: '0.6' }, + ref: elem => { + const keybindingLabel = store.add(new KeybindingLabel(elem, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions })); + store.add(autorun(reader => { + keybindingLabel.set(props.keybinding.read(reader)); + })); + } + }) + ])); +} + +function separator() { + return n.div({ + class: 'menu-separator', + style: { + color: 'var(--vscode-editorActionList-foreground)', + padding: '2px 0', + } + }, n.div({ + style: { + borderBottom: '1px solid var(--vscode-editorHoverWidget-border)', + } + })); +} 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 a3d7dcddbc1..e0a1ce126d0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/gutterIndicatorView.ts @@ -6,16 +6,21 @@ import { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IObservable, constObservable, derived, observableFromEvent } from '../../../../../../base/common/observable.js'; +import { IObservable, autorun, constObservable, derived, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; +import { IHoverService } from '../../../../../../platform/hover/browser/hover.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { buttonBackground, buttonForeground, buttonSecondaryBackground, buttonSecondaryForeground } from '../../../../../../platform/theme/common/colorRegistry.js'; import { registerColor, transparent } from '../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { Rect } from '../../../../../browser/rect.js'; +import { HoverService } from '../../../../../browser/services/hoverService/hoverService.js'; +import { HoverWidget } from '../../../../../browser/services/hoverService/hoverWidget.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../common/core/offsetRange.js'; import { StickyScrollController } from '../../../../stickyScroll/browser/stickyScrollController.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import { GutterIndicatorMenuContent } from './gutterIndicatorMenu.js'; import { mapOutFalsy, n, rectToProps } from './utils.js'; import { localize } from '../../../../../../nls.js'; export const inlineEditIndicatorPrimaryForeground = registerColor( @@ -62,12 +67,14 @@ export const inlineEditIndicatorBackground = registerColor( localize('inlineEdit.gutterIndicator.background', 'Background color for the inline edit gutter indicator.') ); - export class InlineEditsGutterIndicator extends Disposable { constructor( private readonly _editorObs: ObservableCodeEditor, private readonly _originalRange: IObservable, private readonly _model: IObservable, + private readonly _shouldShowHover: IObservable, + @IHoverService private readonly _hoverService: HoverService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); @@ -77,6 +84,14 @@ export class InlineEditsGutterIndicator extends Disposable { allowEditorOverflow: false, minContentWidthInPx: constObservable(0), })); + + this._register(autorun(reader => { + if (this._shouldShowHover.read(reader)) { + this._showHover(); + } else { + this._hoverService.hideHover(); + } + })); } private readonly _originalRangeObs = mapOutFalsy(this._originalRange); @@ -132,44 +147,88 @@ export class InlineEditsGutterIndicator extends Disposable { private readonly _tabAction = derived(this, reader => { const m = this._model.read(reader); - if (m && m.tabShouldJumpToInlineEdit.read(reader)) { return 'jump' as const; } - if (m && m.tabShouldAcceptInlineEdit.read(reader)) { return 'accept' as const; } + if (this._editorObs.isFocused.read(reader)) { + if (m && m.tabShouldJumpToInlineEdit.read(reader)) { return 'jump' as const; } + if (m && m.tabShouldAcceptInlineEdit.read(reader)) { return 'accept' as const; } + } return 'inactive' as const; }); private readonly _onClickAction = derived(this, reader => { if (this._layout.map(d => d && d.docked).read(reader)) { return { - label: 'Click to accept inline edit', + selectionOverride: 'accept' as const, action: () => { this._model.get()?.accept(); } }; } else { return { - label: 'Click to jump to inline edit', + selectionOverride: 'jump' as const, action: () => { this._model.get()?.jump(); } }; } }); + private readonly _iconRef = n.ref(); + private _hoverVisible: boolean = false; + private readonly _isHoveredOverIcon = observableValue(this, false); + private readonly _hoverSelectionOverride = derived(this, reader => this._isHoveredOverIcon.read(reader) ? this._onClickAction.read(reader).selectionOverride : undefined); + + private _showHover(): void { + if (this._hoverVisible) { + return; + } + + const content = this._instantiationService.createInstance( + GutterIndicatorMenuContent, + this._hoverSelectionOverride, + (focusEditor) => { + h?.dispose(); + if (focusEditor) { + this._editorObs.editor.focus(); + } + }, + this._model.map((m, r) => m?.state.read(r)?.inlineCompletion?.inlineCompletion.source.inlineCompletions.commands), + ).toDisposableLiveElement(); + const h = this._hoverService.showHover({ + target: this._iconRef.element, + content: content.element, + }) as HoverWidget | undefined; + if (h) { + this._hoverVisible = true; + h.onDispose(() => { + content.dispose(); + this._hoverVisible = false; + }); + } else { + content.dispose(); + } + } + private readonly _indicator = n.div({ class: 'inline-edits-view-gutter-indicator', onclick: () => this._onClickAction.get().action(), - title: this._onClickAction.map(a => a.label), style: { position: 'absolute', overflow: 'visible', }, - }, mapOutFalsy(this._layout).map(l => !l ? [] : [ + }, mapOutFalsy(this._layout).map(layout => !layout ? [] : [ n.div({ style: { position: 'absolute', background: 'var(--vscode-inlineEdit-gutterIndicator-background)', borderRadius: '4px', - ...rectToProps(reader => l.read(reader).rect), + ...rectToProps(reader => layout.read(reader).rect), } }), n.div({ class: 'icon', + ref: this._iconRef, + onmouseenter: () => { + // TODO show hover when hovering ghost text etc. + this._isHoveredOverIcon.set(true, undefined); + this._showHover(); + }, + onmouseleave: () => { this._isHoveredOverIcon.set(false, undefined); }, style: { cursor: 'pointer', zIndex: '1000', @@ -192,12 +251,12 @@ export class InlineEditsGutterIndicator extends Disposable { display: 'flex', justifyContent: 'center', transition: 'background-color 0.2s ease-in-out', - ...rectToProps(reader => l.read(reader).iconRect), + ...rectToProps(reader => layout.read(reader).iconRect), } }, [ n.div({ style: { - rotate: l.map(l => { + rotate: layout.map(l => { switch (l.arrowDirection) { case 'right': return '0deg'; case 'bottom': return '90deg'; @@ -207,7 +266,7 @@ export class InlineEditsGutterIndicator extends Disposable { transition: 'rotate 0.2s ease-in-out', } }, [ - renderIcon(Codicon.arrowRight), + renderIcon(Codicon.arrowRight) ]) ]), ])).keepUpdated(this._store); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts index 7198e4870e9..e1feaa2eb8b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/sideBySideDiff.ts @@ -180,13 +180,15 @@ export class InlineEditsSideBySideDiff extends Disposable { private readonly _editorContainerTopLeft = observableValue | undefined>(this, undefined); private readonly _editorContainer = n.div({ - class: 'editorContainer', + class: ['editorContainer', this._editorObs.getOption(EditorOption.inlineSuggest).map(v => !v.edits.experimental.useGutterIndicator && 'showHover')], style: { position: 'absolute' }, }, [ n.div({ class: 'preview', style: {}, ref: this.previewRef }), n.div({ class: 'toolbar', style: {}, ref: this.toolbarRef }), ]).keepUpdated(this._store); + public readonly isHovered = this._editorContainer.getIsHovered(this._store); + protected readonly _toolbar = this._register(this._instantiationService.createInstance(CustomizedMenuWorkbenchToolBar, this.toolbarRef.element, MenuId.InlineEditsActions, { menuOptions: { renderShortTitle: true }, toolbarOptions: { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts index a70a0721491..57e0eb3393e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getDomNodePagePosition, h, isSVGElement } from '../../../../../../base/browser/dom.js'; +import { addDisposableListener, getDomNodePagePosition, h, isSVGElement } from '../../../../../../base/browser/dom.js'; import { KeybindingLabel, unthemedKeybindingLabelOptions } from '../../../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; import { numberComparator } from '../../../../../../base/common/arrays.js'; import { findFirstMin } from '../../../../../../base/common/arraysFind.js'; import { BugIndicatingError } from '../../../../../../base/common/errors.js'; -import { Disposable, DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { DisposableStore, IDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; import { derived, derivedObservableWithCache, IObservable, IReader, observableValue, transaction } from '../../../../../../base/common/observable.js'; import { OS } from '../../../../../../base/common/platform.js'; import { getIndentationLength, splitLines } from '../../../../../../base/common/strings.js'; @@ -164,7 +164,7 @@ export class PathBuilder { } type Value = T | IObservable; -type ValueOrList = Value | Value[]; +type ValueOrList = Value | ValueOrList[]; type ValueOrList2 = ValueOrList | ValueOrList>; type Element = HTMLElement | SVGElement; @@ -203,16 +203,18 @@ type SVGElementTagNameMap2 = { type DomTagCreateFn> = ( tag: TTag, - attributes: ElementAttributeKeys & { class?: Value; ref?: IRef }, - children?: ValueOrList2, + attributes: ElementAttributeKeys & { class?: ValueOrList; ref?: IRef }, + children?: ChildNode, ) => ObserverNode; type DomCreateFn = ( - attributes: ElementAttributeKeys & { class?: Value; ref?: IRef }, - children?: ValueOrList2, + attributes: ElementAttributeKeys & { class?: ValueOrList; ref?: IRef }, + children?: ChildNode, ) => ObserverNode; +export type ChildNode = ValueOrList2; + export namespace n { function nodeNs>(elementNs: string | undefined = undefined): DomTagCreateFn { return (tag, attributes, children) => { @@ -233,35 +235,37 @@ export namespace n { } export const div: DomCreateFn = node('div'); + + export const elem = nodeNs(undefined); + export const svg: DomCreateFn = node('svg', 'http://www.w3.org/2000/svg'); export const svgElem = nodeNs('http://www.w3.org/2000/svg'); - export function ref(): Ref { - return new Ref(); + export function ref(): IRefWithVal { + let value: T | undefined = undefined; + const result: IRef = function (val: T) { + value = val; + }; + Object.defineProperty(result, 'element', { + get() { + if (!value) { + throw new BugIndicatingError('Make sure the ref is set before accessing the element. Maybe wrong initialization order?'); + } + return value; + } + }); + return result as any; } } -export interface IRef { - setValue(value: T): void; -} - -export class Ref implements IRef { - private _value: T | undefined = undefined; - - public setValue(value: T): void { - this._value = value; - } +export type IRef = (value: T) => void; - public get element(): T { - if (!this._value) { - throw new BugIndicatingError('Make sure the ref is set before accessing the element. Maybe wrong initialization order?'); - } - return this._value; - } +export interface IRefWithVal extends IRef { + readonly element: T; } -export abstract class ObserverNode extends Disposable { +export abstract class ObserverNode { private readonly _deriveds: (IObservable)[] = []; protected readonly _element: T; @@ -270,48 +274,23 @@ export abstract class ObserverNode extends Disposab tag: string, ref: IRef | undefined, ns: string | undefined, - className: Value | undefined, + className: ValueOrList | undefined, attributes: ElementAttributeKeys, - children: ValueOrList2 | undefined, + children: ChildNode, ) { - super(); - this._element = (ns ? document.createElementNS(ns, tag) : document.createElement(tag)) as unknown as T; if (ref) { - ref.setValue(this._element); - } - - function setClassName(domNode: Element, className: string | string[]) { - if (isSVGElement(domNode)) { - if (Array.isArray(className)) { - domNode.setAttribute('class', className.join(' ')); - } else { - domNode.setAttribute('class', className); - } - } else { - if (Array.isArray(className)) { - domNode.className = className.join(' '); - } else { - domNode.className = className; - } - } + ref(this._element); } if (className) { - if (isObservable(className)) { + if (hasObservable(className)) { this._deriveds.push(derived(this, reader => { - setClassName(this._element, className.read(reader)); + setClassName(this._element, getClassName(className, reader)); })); } else { - setClassName(this._element, className); - } - } - - function convertCssValue(value: any): string { - if (typeof value === 'number') { - return value + 'px'; + setClassName(this._element, getClassName(className, undefined)); } - return value; } for (const [key, value] of Object.entries(attributes)) { @@ -347,36 +326,26 @@ export abstract class ObserverNode extends Disposab } } - function getChildren(reader: IReader | undefined, children: ValueOrList2): (Element | string)[] { - if (isObservable(children)) { - return getChildren(reader, children.read(reader)); - } - if (Array.isArray(children)) { - return children.flatMap(c => getChildren(reader, c)); - } - if (children instanceof ObserverNode) { - if (reader) { - children.readEffect(reader); + if (children) { + function getChildren(reader: IReader | undefined, children: ValueOrList2): (Element | string)[] { + if (isObservable(children)) { + return getChildren(reader, children.read(reader)); } - return [children._element]; - } - if (children) { - return [children]; - } - return []; - } - - function childrenIsObservable(children: ValueOrList2): boolean { - if (isObservable(children)) { - return true; - } - if (Array.isArray(children)) { - return children.some(c => childrenIsObservable(c)); + if (Array.isArray(children)) { + return children.flatMap(c => getChildren(reader, c)); + } + if (children instanceof ObserverNode) { + if (reader) { + children.readEffect(reader); + } + return [children._element]; + } + if (children) { + return [children]; + } + return []; } - return false; - } - if (children) { const d = derived(this, reader => { this._element.replaceChildren(...getChildren(reader, children)); }); @@ -399,12 +368,102 @@ export abstract class ObserverNode extends Disposab }).recomputeInitiallyAndOnChange(store); return this as unknown as ObserverNodeWithElement; } + + /** + * Creates a live element that will keep the element updated as long as the returned object is not disposed. + */ + toDisposableLiveElement() { + const store = new DisposableStore(); + this.keepUpdated(store); + return new LiveElement(this._element, store); + } +} + + + +function setClassName(domNode: Element, className: string) { + if (isSVGElement(domNode)) { + domNode.setAttribute('class', className); + } else { + domNode.className = className; + } +} + +function resolve(value: ValueOrList, reader: IReader | undefined, cb: (val: T) => void): void { + if (isObservable(value)) { + cb(value.read(reader)); + } + if (Array.isArray(value)) { + for (const v of value) { + resolve(v, reader, cb); + } + } + cb(value as any); +} + +function getClassName(className: ValueOrList | undefined, reader: IReader | undefined): string { + let result = ''; + resolve(className, reader, val => { + if (val) { + if (result.length === 0) { + result = val; + } else { + result += ' ' + val; + } + } + }); + return result; +} + +function hasObservable(value: ValueOrList): boolean { + if (isObservable(value)) { + return true; + } + if (Array.isArray(value)) { + return value.some(v => hasObservable(v)); + } + return false; +} +function convertCssValue(value: any): string { + if (typeof value === 'number') { + return value + 'px'; + } + return value; +} + + +function childrenIsObservable(children: ValueOrList2): boolean { + if (isObservable(children)) { + return true; + } + if (Array.isArray(children)) { + return children.some(c => childrenIsObservable(c)); + } + return false; +} + +export class LiveElement { + constructor( + public readonly element: T, + private readonly _disposable: IDisposable, + ) { } + + dispose() { + this._disposable.dispose(); + } } export class ObserverNodeWithElement extends ObserverNode { public get element() { return this._element; } + + public getIsHovered(store: DisposableStore): IObservable { + const hovered = observableValue('hovered', false); + store.add(addDisposableListener(this._element, 'mouseenter', () => hovered.set(true, undefined))); + store.add(addDisposableListener(this._element, 'mouseleave', () => hovered.set(false, undefined))); + return hovered; + } } function setOrRemoveAttribute(element: Element, key: string, value: unknown) { @@ -479,3 +538,5 @@ export function rectToProps(fn: (reader: IReader) => Rect) { height: derived(reader => fn(reader).bottom - fn(reader).top), }; } + +export type FirstFnArg = T extends (arg: infer U) => any ? U : never; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css index e5531dc7b4b..0a46a149042 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.css @@ -77,7 +77,7 @@ } .inline-edits-view { - &.toolbarDropdownVisible, .editorContainer:hover { + &.toolbarDropdownVisible, .editorContainer.showHover:hover { .toolbar { display: block; } @@ -165,3 +165,22 @@ border-left: solid var(--vscode-inlineEdit-modifiedChangedTextBackground) 3px; } } + +.monaco-menu-option { + color: var(--vscode-editorActionList-foreground); + font-size: 13px; + padding: 0 10px; + line-height: 26px; + display: flex; + gap: 8px; + align-items: center; + border-radius: 4px; + cursor: pointer; + + &.active { + background: var(--vscode-editorActionList-focusBackground); + color: var(--vscode-editorActionList-focusForeground); + outline: 1px solid var(--vscode-menu-selectionBorder, transparent); + outline-offset: -1px; + } +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts index dfb11c8bebf..4282852af53 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/view.ts @@ -137,10 +137,12 @@ export class InlineEditsView extends Disposable { protected readonly _indicator = this._register(autorunWithStore((reader, store) => { if (this._useGutterIndicator.read(reader)) { - store.add(new InlineEditsGutterIndicator( + store.add(this._instantiationService.createInstance( + InlineEditsGutterIndicator, this._editorObs, this._uiState.map(s => s && s.originalDisplayRange), this._model, + this._sideBySide.isHovered, )); } else { store.add(new InlineEditsIndicator( From 0c51035590b20878d940ceb3cac0d54c4eb54bbc Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 20 Dec 2024 13:59:08 -0800 Subject: [PATCH 460/479] testing: fix errors peeks being oversized relative to their contents (#236763) Fixes #214011 --- src/vs/base/browser/ui/list/listView.ts | 2 + .../contrib/zoneWidget/browser/zoneWidget.ts | 14 ++-- .../contrib/debug/browser/callStackWidget.ts | 8 +++ .../testResultsView/testMessageStack.ts | 49 ------------- .../testResultsView/testResultsViewContent.ts | 37 +++++++--- .../testing/browser/testingOutputPeek.ts | 70 +++++++++++-------- 6 files changed, 85 insertions(+), 95 deletions(-) delete mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testMessageStack.ts diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 36355a377ba..e305b7de4b9 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -567,6 +567,8 @@ export class ListView implements IListView { if (this.supportDynamicHeights) { this._rerender(this.lastRenderTop, this.lastRenderHeight); + } else { + this._onDidChangeContentHeight.fire(this.contentHeight); // otherwise fired in _rerender() } } diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index 51988af7d17..a38f1639eea 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -29,7 +29,6 @@ export interface IOptions { frameColor?: Color | string; arrowColor?: Color; keepEditorSelection?: boolean; - allowUnlimitedHeight?: boolean; ordinal?: number; showInHiddenAreas?: boolean; } @@ -376,6 +375,11 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { return result; } + /** Gets the maximum widget height in lines. */ + protected _getMaximumHeightInLines(): number | undefined { + return Math.max(12, (this.editor.getLayoutInfo().height / this.editor.getOption(EditorOption.lineHeight)) * 0.8); + } + private _showImpl(where: Range, heightInLines: number): void { const position = where.getStartPosition(); const layoutInfo = this.editor.getLayoutInfo(); @@ -389,8 +393,8 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { const lineHeight = this.editor.getOption(EditorOption.lineHeight); // adjust heightInLines to viewport - if (!this.options.allowUnlimitedHeight) { - const maxHeightInLines = Math.max(12, (this.editor.getLayoutInfo().height / lineHeight) * 0.8); + const maxHeightInLines = this._getMaximumHeightInLines(); + if (maxHeightInLines !== undefined) { heightInLines = Math.min(heightInLines, maxHeightInLines); } @@ -493,7 +497,9 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { // implement in subclass } - protected _relayout(newHeightInLines: number): void { + protected _relayout(_newHeightInLines: number): void { + const maxHeightInLines = this._getMaximumHeightInLines(); + const newHeightInLines = maxHeightInLines === undefined ? _newHeightInLines : Math.min(maxHeightInLines, _newHeightInLines); if (this._viewZone && this._viewZone.heightInLines !== newHeightInLines) { this.editor.changeViewZones(accessor => { if (this._viewZone) { diff --git a/src/vs/workbench/contrib/debug/browser/callStackWidget.ts b/src/vs/workbench/contrib/debug/browser/callStackWidget.ts index dc2c12dd730..2948bbb715b 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackWidget.ts @@ -119,10 +119,18 @@ export class CallStackWidget extends Disposable { private readonly currentFramesDs = this._register(new DisposableStore()); private cts?: CancellationTokenSource; + public get onDidChangeContentHeight() { + return this.list.onDidChangeContentHeight; + } + public get onDidScroll() { return this.list.onDidScroll; } + public get contentHeight() { + return this.list.contentHeight; + } + constructor( container: HTMLElement, containingEditor: ICodeEditor | undefined, diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testMessageStack.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testMessageStack.ts deleted file mode 100644 index fe0592cb567..00000000000 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testMessageStack.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { AnyStackFrame, CallStackFrame, CallStackWidget } from '../../../debug/browser/callStackWidget.js'; -import { ITestMessageStackFrame } from '../../common/testTypes.js'; - -export class TestResultStackWidget extends Disposable { - private readonly widget: CallStackWidget; - - public get onDidScroll() { - return this.widget.onDidScroll; - } - - constructor( - private readonly container: HTMLElement, - containingEditor: ICodeEditor | undefined, - @IInstantiationService instantiationService: IInstantiationService, - ) { - super(); - - this.widget = this._register(instantiationService.createInstance( - CallStackWidget, - container, - containingEditor, - )); - } - - public collapseAll() { - this.widget.collapseAll(); - } - - public update(messageFrame: AnyStackFrame, stack: ITestMessageStackFrame[]) { - this.widget.setFrames([messageFrame, ...stack.map(frame => new CallStackFrame( - frame.label, - frame.uri, - frame.position?.lineNumber, - frame.position?.column, - ))]); - } - - public layout(height?: number, width?: number) { - this.widget.layout(height ?? this.container.clientHeight, width); - } -} diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts index 421afad78f2..5f2990a413a 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts @@ -14,7 +14,6 @@ import { Emitter, Event, Relay } from '../../../../../base/common/event.js'; import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { observableValue } from '../../../../../base/common/observable.js'; -import './testResultsViewContent.css'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; import { localize } from '../../../../../nls.js'; @@ -28,12 +27,7 @@ import { IInstantiationService, ServicesAccessor } from '../../../../../platform import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; import { IQuickInputService } from '../../../../../platform/quickinput/common/quickInput.js'; import { IUriIdentityService } from '../../../../../platform/uriIdentity/common/uriIdentity.js'; -import { CustomStackFrame } from '../../../debug/browser/callStackWidget.js'; -import * as icons from '../icons.js'; -import { TestResultStackWidget } from './testMessageStack.js'; -import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from './testResultsOutput.js'; -import { equalsSubject, getSubjectTestItem, InspectSubject, MessageSubject, TaskSubject, TestOutputSubject } from './testResultsSubject.js'; -import { OutputPeekTree } from './testResultsTree.js'; +import { AnyStackFrame, CallStackFrame, CallStackWidget, CustomStackFrame } from '../../../debug/browser/callStackWidget.js'; import { TestCommandId } from '../../common/constants.js'; import { IObservableValue } from '../../common/observableValue.js'; import { capabilityContextKeys, ITestProfileService } from '../../common/testProfileService.js'; @@ -41,6 +35,11 @@ import { LiveTestResult } from '../../common/testResult.js'; import { ITestFollowup, ITestService } from '../../common/testService.js'; import { ITestMessageStackFrame, TestRunProfileBitset } from '../../common/testTypes.js'; import { TestingContextKeys } from '../../common/testingContextKeys.js'; +import * as icons from '../icons.js'; +import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from './testResultsOutput.js'; +import { equalsSubject, getSubjectTestItem, InspectSubject, MessageSubject, TaskSubject, TestOutputSubject } from './testResultsSubject.js'; +import { OutputPeekTree } from './testResultsTree.js'; +import './testResultsViewContent.css'; const enum SubView { Diff = 0, @@ -183,7 +182,7 @@ export class TestResultsViewContent extends Disposable { private contextKeyTestMessage!: IContextKey; private contextKeyResultOutdated!: IContextKey; private stackContainer!: HTMLElement; - private callStackWidget!: TestResultStackWidget; + private callStackWidget!: CallStackWidget; private currentTopFrame?: MessageStackFrame; private isDoingLayoutUpdate?: boolean; @@ -209,6 +208,14 @@ export class TestResultsViewContent extends Disposable { }; } + public get onDidChangeContentHeight() { + return this.callStackWidget.onDidChangeContentHeight; + } + + public get contentHeight() { + return this.callStackWidget?.contentHeight || 0; + } + constructor( private readonly editor: ICodeEditor | undefined, private readonly options: { @@ -233,7 +240,7 @@ export class TestResultsViewContent extends Disposable { const messageContainer = this.messageContainer = dom.$('.test-output-peek-message-container'); this.stackContainer = dom.append(containerElement, dom.$('.test-output-call-stack-container')); - this.callStackWidget = this._register(this.instantiationService.createInstance(TestResultStackWidget, this.stackContainer, this.editor)); + this.callStackWidget = this._register(this.instantiationService.createInstance(CallStackWidget, this.stackContainer, this.editor)); this.followupWidget = this._register(this.instantiationService.createInstance(FollowupActionWidget, this.editor)); this.onCloseEmitter.input = this.followupWidget.onClose; @@ -315,13 +322,22 @@ export class TestResultsViewContent extends Disposable { this.currentSubjectStore.clear(); const callFrames = this.getCallFrames(opts.subject) || []; const topFrame = await this.prepareTopFrame(opts.subject, callFrames); - this.callStackWidget.update(topFrame, callFrames); + this.setCallStackFrames(topFrame, callFrames); this.followupWidget.show(opts.subject); this.populateFloatingClick(opts.subject); }); } + private setCallStackFrames(messageFrame: AnyStackFrame, stack: ITestMessageStackFrame[]) { + this.callStackWidget.setFrames([messageFrame, ...stack.map(frame => new CallStackFrame( + frame.label, + frame.uri, + frame.position?.lineNumber, + frame.position?.column, + ))]); + } + /** * Collapses all displayed stack frames. */ @@ -375,7 +391,6 @@ export class TestResultsViewContent extends Disposable { })); } - if (provider.onDidContentSizeChange) { this.currentSubjectStore.add(provider.onDidContentSizeChange(() => { if (this.dimension && !this.isDoingLayoutUpdate) { diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 3ea9c37de5c..808e68a6ca9 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -6,16 +6,16 @@ import * as dom from '../../../../base/browser/dom.js'; import { alert } from '../../../../base/browser/ui/aria/aria.js'; import { IAction } from '../../../../base/common/actions.js'; +import { RunOnceScheduler } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Color } from '../../../../base/common/color.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; +import { Event } from '../../../../base/common/event.js'; import { stripIcons } from '../../../../base/common/iconLabels.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { Lazy } from '../../../../base/common/lazy.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { derived, disposableObservableValue, observableValue } from '../../../../base/common/observable.js'; -import { count } from '../../../../base/common/strings.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EditorAction2 } from '../../../../editor/browser/editorExtensions.js'; @@ -678,10 +678,8 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo class TestResultsPeek extends PeekViewWidget { - private static lastHeightInLines?: number; - - private readonly visibilityChange = this._disposables.add(new Emitter()); public readonly current = observableValue('testPeekCurrent', undefined); + private resizeOnNextContentHeightUpdate = false; private content!: TestResultsViewContent; private scopedContextKeyService!: IContextKeyService; private dimension?: dom.Dimension; @@ -701,10 +699,22 @@ class TestResultsPeek extends PeekViewWidget { super(editor, { showFrame: true, frameWidth: 1, showArrow: true, isResizeable: true, isAccessible: true, className: 'test-output-peek' }, instantiationService); this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme, this)); - this._disposables.add(this.onDidClose(() => this.visibilityChange.fire(false))); peekViewService.addExclusiveWidget(editor, this); } + protected override _getMaximumHeightInLines(): number | undefined { + const defaultMaxHeight = super._getMaximumHeightInLines(); + const contentHeight = this.content?.contentHeight; + if (!contentHeight) { // undefined or 0 + return defaultMaxHeight; + } + + const lineHeight = this.editor.getOption(EditorOption.lineHeight); + // 41 is experimentally determined to be the overhead of the peek view itself + // to avoid showing scrollbars by default in its content. + return Math.min(defaultMaxHeight || Infinity, (contentHeight + 41) / lineHeight); + } + private applyTheme() { const theme = this.themeService.getColorTheme(); const current = this.current.get(); @@ -764,6 +774,27 @@ class TestResultsPeek extends PeekViewWidget { protected override _fillBody(containerElement: HTMLElement): void { this.content.fillBody(containerElement); + + // Resize on height updates for a short time to allow any heights made + // by editor contributions to come into effect before. + const contentHeightSettleTimer = this._disposables.add(new RunOnceScheduler(() => { + this.resizeOnNextContentHeightUpdate = false; + }, 500)); + + this._disposables.add(this.content.onDidChangeContentHeight(height => { + if (!this.resizeOnNextContentHeightUpdate || !height) { + return; + } + + const displayed = this._getMaximumHeightInLines(); + if (displayed) { + this._relayout(Math.min(displayed, this.getVisibleEditorLines() / 2)); + if (!contentHeightSettleTimer.isScheduled()) { + contentHeightSettleTimer.schedule(); + } + } + })); + this._disposables.add(this.content.onDidRequestReveal(sub => { TestingOutputPeekController.get(this.editor)?.show(sub instanceof MessageSubject ? sub.messageUri @@ -780,7 +811,6 @@ class TestResultsPeek extends PeekViewWidget { return this.showInPlace(subject); } - const message = subject.message; const previous = this.current; const revealLocation = subject.revealLocation?.range.getStartPosition(); if (!revealLocation && !previous) { @@ -792,13 +822,8 @@ class TestResultsPeek extends PeekViewWidget { return this.showInPlace(subject); } - // If there is a stack we want to display, ensure the default size is large-ish - const peekLines = TestResultsPeek.lastHeightInLines || Math.max( - inspectSubjectHasStack(subject) ? Math.ceil(this.getVisibleEditorLines() / 2) : 0, - hintMessagePeekHeight(message) - ); - - this.show(revealLocation, peekLines); + this.resizeOnNextContentHeightUpdate = true; + this.show(revealLocation, 10); // 10 is just a random number, we resize once content is available this.editor.revealRangeNearTopIfOutsideViewport(Range.fromPositions(revealLocation), ScrollType.Smooth); return this.showInPlace(subject); @@ -832,11 +857,6 @@ class TestResultsPeek extends PeekViewWidget { await this.content.reveal({ subject, preserveFocus: false }); } - protected override _relayout(newHeightInLines: number): void { - super._relayout(newHeightInLines); - TestResultsPeek.lastHeightInLines = newHeightInLines; - } - /** @override */ protected override _doLayoutBody(height: number, width: number) { super._doLayoutBody(height, width); @@ -919,23 +939,11 @@ export class TestResultsView extends ViewPane { } } -const hintMessagePeekHeight = (msg: ITestMessage) => { - const msgHeight = ITestMessage.isDiffable(msg) - ? Math.max(hintPeekStrHeight(msg.actual), hintPeekStrHeight(msg.expected)) - : hintPeekStrHeight(typeof msg.message === 'string' ? msg.message : msg.message.value); - - // add 8ish lines for the size of the title and decorations in the peek. - return msgHeight + 8; -}; - const firstLine = (str: string) => { const index = str.indexOf('\n'); return index === -1 ? str : str.slice(0, index); }; - -const hintPeekStrHeight = (str: string) => Math.min(count(str, '\n'), 24); - function getOuterEditorFromDiffEditor(codeEditorService: ICodeEditorService): ICodeEditor | null { const diffEditors = codeEditorService.listDiffEditors(); From 13d2a615cb98a1dc3371dcea2f03d8e65b2d83e6 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 20 Dec 2024 14:33:52 -0800 Subject: [PATCH 461/479] fix: don't register Cloud Changes action if not configured in product.json (#236764) --- .../contrib/editSessions/browser/editSessionsStorageService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index b056dbf64a2..1bb5eb33737 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -455,6 +455,9 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } private registerSignInAction() { + if (!this.serverConfiguration?.url) { + return; + } const that = this; const id = 'workbench.editSessions.actions.signIn'; const when = ContextKeyExpr.and(ContextKeyExpr.equals(EDIT_SESSIONS_PENDING_KEY, false), ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false)); From 68252d3d7b25bc50c67abad5f32c885012deb985 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 20 Dec 2024 20:22:20 -0800 Subject: [PATCH 462/479] fix: don't leak listener in share contribution (#236767) --- .../contrib/share/browser/share.contribution.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/share/browser/share.contribution.ts b/src/vs/workbench/contrib/share/browser/share.contribution.ts index cd1cfc176e0..3566792aa9d 100644 --- a/src/vs/workbench/contrib/share/browser/share.contribution.ts +++ b/src/vs/workbench/contrib/share/browser/share.contribution.ts @@ -32,7 +32,7 @@ import { IProgressService, ProgressLocation } from '../../../../platform/progres import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../../platform/configuration/common/configurationRegistry.js'; import { workbenchConfigurationNodeBase } from '../../../common/configuration.js'; -import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; const targetMenus = [ MenuId.EditorContextShare, @@ -44,7 +44,7 @@ const targetMenus = [ MenuId.ExplorerContextShare ]; -class ShareWorkbenchContribution { +class ShareWorkbenchContribution extends Disposable { private static SHARE_ENABLED_SETTING = 'workbench.experimental.share.enabled'; private _disposables: DisposableStore | undefined; @@ -53,10 +53,12 @@ class ShareWorkbenchContribution { @IShareService private readonly shareService: IShareService, @IConfigurationService private readonly configurationService: IConfigurationService ) { + super(); + if (this.configurationService.getValue(ShareWorkbenchContribution.SHARE_ENABLED_SETTING)) { this.registerActions(); } - this.configurationService.onDidChangeConfiguration(e => { + this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(ShareWorkbenchContribution.SHARE_ENABLED_SETTING)) { const settingValue = this.configurationService.getValue(ShareWorkbenchContribution.SHARE_ENABLED_SETTING); if (settingValue === true && this._disposables === undefined) { @@ -66,7 +68,12 @@ class ShareWorkbenchContribution { this._disposables = undefined; } } - }); + })); + } + + override dispose(): void { + super.dispose(); + this._disposables?.dispose(); } private registerActions() { From d4de5ceba46823bafe63733df924e63990afef72 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 20 Dec 2024 20:23:36 -0800 Subject: [PATCH 463/479] test: adopt `ensureNoDisposablesAreLeakedInTestSuite` (#236766) * test: adopt `ensureNoDisposablesAreLeakedInTestSuite` * test: adopt `ensureNoDisposablesAreLeakedInTestSuite` --- eslint.config.js | 1 - .../editSessions/test/browser/editSessions.test.ts | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index aa9fd4930c5..db83d096edd 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -229,7 +229,6 @@ export default tseslint.config( 'src/vs/workbench/api/test/node/extHostTunnelService.test.ts', 'src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts', 'src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts', - 'src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts', 'src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts', 'src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts', 'src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts', diff --git a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts index 38aeb6c161b..359c7be57d5 100644 --- a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts +++ b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts @@ -50,11 +50,13 @@ import { TestStorageService } from '../../../../test/common/workbenchTestService import { IUriIdentityService } from '../../../../../platform/uriIdentity/common/uriIdentity.js'; import { UriIdentityService } from '../../../../../platform/uriIdentity/common/uriIdentityService.js'; import { IWorkspaceIdentityService, WorkspaceIdentityService } from '../../../../services/workspaces/common/workspaceIdentityService.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; const folderName = 'test-folder'; const folderUri = URI.file(`/${folderName}`); suite('Edit session sync', () => { + let instantiationService: TestInstantiationService; let editSessionsContribution: EditSessionsContribution; let fileService: FileService; @@ -63,6 +65,7 @@ suite('Edit session sync', () => { const disposables = new DisposableStore(); suiteSetup(() => { + sandbox = sinon.createSandbox(); instantiationService = new TestInstantiationService(); @@ -172,6 +175,10 @@ suite('Edit session sync', () => { disposables.clear(); }); + suiteTeardown(() => { + disposables.dispose(); + }); + test('Can apply edit session', async function () { const fileUri = joinPath(folderUri, 'dir1', 'README.md'); const fileContents = '# readme'; @@ -218,4 +225,6 @@ suite('Edit session sync', () => { // Verify that we did not attempt to write the edit session assert.equal(writeStub.called, false); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); From d6a59b79698b81cc1f0a74113189b7106453bb78 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Sat, 21 Dec 2024 12:59:52 -0800 Subject: [PATCH 464/479] debug: fix mismatched indentation for folders in loaded scripts (#236750) * debug: fix mismatched indentation for folders in loaded scripts Fixes #228241 * fix test --- .../contrib/debug/browser/baseDebugView.ts | 2 +- .../debug/browser/loadedScriptsView.ts | 78 ++++++++++--------- .../debug/test/browser/baseDebugView.test.ts | 2 +- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 9576b7e5bb7..d5ba5f0c856 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -51,7 +51,7 @@ export interface IVariableTemplateData { export function renderViewTree(container: HTMLElement): HTMLElement { const treeContainer = $('.'); - treeContainer.classList.add('debug-view-content'); + treeContainer.classList.add('debug-view-content', 'file-icon-themable-tree'); container.appendChild(treeContainer); return treeContainer; } diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 3c6e37f7bc0..4876cc3a797 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -3,47 +3,46 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from '../../../../nls.js'; -import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js'; -import { normalize, isAbsolute, posix } from '../../../../base/common/path.js'; -import { ViewPane, ViewAction } from '../../../browser/parts/views/viewPane.js'; -import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { renderViewTree } from './baseDebugView.js'; -import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE, LOADED_SCRIPTS_VIEW_ID } from '../common/debug.js'; -import { Source } from '../common/debugSource.js'; -import { IWorkspaceContextService, IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js'; -import { IContextKey, IContextKeyService, ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; -import { normalizeDriveLetter, tildify } from '../../../../base/common/labels.js'; -import { isWindows } from '../../../../base/common/platform.js'; -import { URI } from '../../../../base/common/uri.js'; -import { ltrim } from '../../../../base/common/strings.js'; -import { RunOnceScheduler } from '../../../../base/common/async.js'; -import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel } from '../../../browser/labels.js'; -import { FileKind } from '../../../../platform/files/common/files.js'; import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; -import { ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, ITreeElement } from '../../../../base/browser/ui/tree/tree.js'; import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { WorkbenchCompressibleObjectTree } from '../../../../platform/list/browser/listService.js'; -import { dispose } from '../../../../base/common/lifecycle.js'; -import { createMatches, FuzzyScore } from '../../../../base/common/filters.js'; -import { DebugContentProvider } from '../common/debugContentProvider.js'; -import { ILabelService } from '../../../../platform/label/common/label.js'; +import { TreeFindMode } from '../../../../base/browser/ui/tree/abstractTree.js'; import type { ICompressedTreeNode } from '../../../../base/browser/ui/tree/compressedObjectTreeModel.js'; import type { ICompressibleTreeRenderer } from '../../../../base/browser/ui/tree/objectTree.js'; -import { registerAction2, MenuId } from '../../../../platform/actions/common/actions.js'; +import { ITreeElement, ITreeFilter, ITreeNode, TreeFilterResult, TreeVisibility } from '../../../../base/browser/ui/tree/tree.js'; +import { RunOnceScheduler } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; - -import { IViewDescriptorService } from '../../../common/views.js'; +import { createMatches, FuzzyScore } from '../../../../base/common/filters.js'; +import { normalizeDriveLetter, tildify } from '../../../../base/common/labels.js'; +import { dispose } from '../../../../base/common/lifecycle.js'; +import { isAbsolute, normalize, posix } from '../../../../base/common/path.js'; +import { isWindows } from '../../../../base/common/platform.js'; +import { ltrim } from '../../../../base/common/strings.js'; +import { URI } from '../../../../base/common/uri.js'; +import * as nls from '../../../../nls.js'; +import { MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; +import { FileKind } from '../../../../platform/files/common/files.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; +import { ILabelService } from '../../../../platform/label/common/label.js'; +import { WorkbenchCompressibleObjectTree } from '../../../../platform/list/browser/listService.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { IFileIconTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { IWorkspaceContextService, IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js'; +import { IResourceLabel, IResourceLabelOptions, IResourceLabelProps, ResourceLabels } from '../../../browser/labels.js'; +import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js'; +import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js'; +import { IViewDescriptorService } from '../../../common/views.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IPathService } from '../../../services/path/common/pathService.js'; -import { TreeFindMode } from '../../../../base/browser/ui/tree/abstractTree.js'; -import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { CONTEXT_LOADED_SCRIPTS_ITEM_TYPE, IDebugService, IDebugSession, LOADED_SCRIPTS_VIEW_ID } from '../common/debug.js'; +import { DebugContentProvider } from '../common/debugContentProvider.js'; +import { Source } from '../common/debugSource.js'; +import { renderViewTree } from './baseDebugView.js'; const NEW_STYLE_COMPRESS = true; @@ -439,7 +438,7 @@ export class LoadedScriptsView extends ViewPane { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, - @IHoverService hoverService: IHoverService + @IHoverService hoverService: IHoverService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); @@ -449,8 +448,7 @@ export class LoadedScriptsView extends ViewPane { super.renderBody(container); this.element.classList.add('debug-pane'); - container.classList.add('debug-loaded-scripts'); - container.classList.add('show-file-icons'); + container.classList.add('debug-loaded-scripts', 'show-file-icons'); this.treeContainer = renderViewTree(container); @@ -461,6 +459,14 @@ export class LoadedScriptsView extends ViewPane { this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); this._register(this.treeLabels); + const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => { + this.treeContainer.classList.toggle('align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons); + this.treeContainer.classList.toggle('hide-arrows', fileIconTheme.hidesExplorerArrows === true); + }; + + this._register(this.themeService.onDidFileIconThemeChange(onFileIconThemeChange)); + onFileIconThemeChange(this.themeService.getFileIconTheme()); + this.tree = this.instantiationService.createInstance(WorkbenchCompressibleObjectTree, 'LoadedScriptsView', this.treeContainer, diff --git a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts index fcefad3c516..9122412e421 100644 --- a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts @@ -106,7 +106,7 @@ suite('Debug - Base Debug View', () => { const container = $('.container'); const treeContainer = renderViewTree(container); - assert.strictEqual(treeContainer.className, 'debug-view-content'); + assert.strictEqual(treeContainer.className, 'debug-view-content file-icon-themable-tree'); assert.strictEqual(container.childElementCount, 1); assert.strictEqual(container.firstChild, treeContainer); assert.strictEqual(dom.isHTMLDivElement(treeContainer), true); From d953d84d9018aef819147320ed58e5b517d2b0a2 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Sat, 21 Dec 2024 13:01:00 -0800 Subject: [PATCH 465/479] debug: allow filter/search on debug values (#236768) * debug: allow filter/search on debug values I think this is something that never worked, or at least not for a long while. Implementing match highlighting in values in the era of linkification and ANSI support was a little more complex, but this works well now. ![](https://memes.peet.io/img/24-12-b1f699dc-f20c-4c93-a5ce-f768473fff62.png) Fixes #230945 * fix test --- .../contrib/debug/browser/baseDebugView.ts | 30 ++++++- .../debug/browser/debugANSIHandling.ts | 29 ++++-- .../debug/browser/debugExpressionRenderer.ts | 13 +-- .../contrib/debug/browser/linkDetector.ts | 89 +++++++++++++++---- .../debug/browser/media/debugViewlet.css | 6 ++ .../contrib/debug/browser/variablesView.ts | 4 +- .../debug/browser/watchExpressionsView.ts | 4 +- .../test/browser/debugANSIHandling.test.ts | 8 +- .../debug/test/browser/linkDetector.test.ts | 64 +++++++++++++ 9 files changed, 208 insertions(+), 39 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index d5ba5f0c856..efb22ae81c1 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -9,19 +9,21 @@ import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { HighlightedLabel, IHighlight } from '../../../../base/browser/ui/highlightedlabel/highlightedLabel.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { IInputValidationOptions, InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js'; +import { IKeyboardNavigationLabelProvider } from '../../../../base/browser/ui/list/list.js'; import { IAsyncDataSource, ITreeNode, ITreeRenderer } from '../../../../base/browser/ui/tree/tree.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { FuzzyScore, createMatches } from '../../../../base/common/filters.js'; import { createSingleCallFunction } from '../../../../base/common/functional.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { DisposableStore, IDisposable, dispose, toDisposable } from '../../../../base/common/lifecycle.js'; +import { removeAnsiEscapeCodes } from '../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { localize } from '../../../../nls.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { defaultInputBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; -import { IDebugService, IExpression } from '../common/debug.js'; +import { IDebugService, IExpression, IScope } from '../common/debug.js'; import { Variable } from '../common/debugModel.js'; import { IDebugVisualizerService } from '../common/debugVisualizers.js'; import { LinkDetector } from './linkDetector.js'; @@ -78,6 +80,32 @@ export interface IExpressionTemplateData { currentElement: IExpression | undefined; } +/** Splits highlights based on matching of the {@link expressionAndScopeLabelProvider} */ +export const splitExpressionOrScopeHighlights = (e: IExpression | IScope, highlights: IHighlight[]) => { + const nameEndsAt = e.name.length; + const labelBeginsAt = e.name.length + 2; + const name: IHighlight[] = []; + const value: IHighlight[] = []; + for (const hl of highlights) { + if (hl.start < nameEndsAt) { + name.push({ start: hl.start, end: Math.min(hl.end, nameEndsAt) }); + } + if (hl.end > labelBeginsAt) { + value.push({ start: Math.max(hl.start - labelBeginsAt, 0), end: hl.end - labelBeginsAt }); + } + } + + return { name, value }; +}; + +/** Keyboard label provider for expression and scope tree elements. */ +export const expressionAndScopeLabelProvider: IKeyboardNavigationLabelProvider = { + getKeyboardNavigationLabel(e) { + const stripAnsi = e.getSession()?.rememberedCapabilities?.supportsANSIStyling; + return `${e.name}: ${stripAnsi ? removeAnsiEscapeCodes(e.value) : e.value}`; + }, +}; + export abstract class AbstractExpressionDataSource implements IAsyncDataSource { constructor( @IDebugService protected debugService: IDebugService, diff --git a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts index 16923a73f61..4db2d7eb8c7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts +++ b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IHighlight } from '../../../../base/browser/ui/highlightedlabel/highlightedLabel.js'; import { Color, RGBA } from '../../../../base/common/color.js'; import { isDefined } from '../../../../base/common/types.js'; import { editorHoverBackground, listActiveSelectionBackground, listFocusBackground, listInactiveFocusBackground, listInactiveSelectionBackground } from '../../../../platform/theme/common/colorRegistry.js'; @@ -16,7 +17,7 @@ import { ILinkDetector } from './linkDetector.js'; * @param text The content to stylize. * @returns An {@link HTMLSpanElement} that contains the potentially stylized text. */ -export function handleANSIOutput(text: string, linkDetector: ILinkDetector, workspaceFolder: IWorkspaceFolder | undefined): HTMLSpanElement { +export function handleANSIOutput(text: string, linkDetector: ILinkDetector, workspaceFolder: IWorkspaceFolder | undefined, highlights: IHighlight[] | undefined): HTMLSpanElement { const root: HTMLSpanElement = document.createElement('span'); const textLength: number = text.length; @@ -27,6 +28,7 @@ export function handleANSIOutput(text: string, linkDetector: ILinkDetector, work let customUnderlineColor: RGBA | string | undefined; let colorsInverted: boolean = false; let currentPos: number = 0; + let unprintedChars = 0; let buffer: string = ''; while (currentPos < textLength) { @@ -58,8 +60,10 @@ export function handleANSIOutput(text: string, linkDetector: ILinkDetector, work if (sequenceFound) { + unprintedChars += 2 + ansiSequence.length; + // Flush buffer with previous styles. - appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor, customUnderlineColor); + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor, customUnderlineColor, highlights, currentPos - buffer.length - unprintedChars); buffer = ''; @@ -105,7 +109,7 @@ export function handleANSIOutput(text: string, linkDetector: ILinkDetector, work // Flush remaining text buffer if not empty. if (buffer) { - appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor, customUnderlineColor); + appendStylizedStringToContainer(root, buffer, styleNames, linkDetector, workspaceFolder, customFgColor, customBgColor, customUnderlineColor, highlights, currentPos - buffer.length); } return root; @@ -395,6 +399,8 @@ export function handleANSIOutput(text: string, linkDetector: ILinkDetector, work * @param customTextColor If provided, will apply custom color with inline style. * @param customBackgroundColor If provided, will apply custom backgroundColor with inline style. * @param customUnderlineColor If provided, will apply custom textDecorationColor with inline style. + * @param highlights The ranges to highlight. + * @param offset The starting index of the stringContent in the original text. */ export function appendStylizedStringToContainer( root: HTMLElement, @@ -402,15 +408,24 @@ export function appendStylizedStringToContainer( cssClasses: string[], linkDetector: ILinkDetector, workspaceFolder: IWorkspaceFolder | undefined, - customTextColor?: RGBA | string, - customBackgroundColor?: RGBA | string, - customUnderlineColor?: RGBA | string, + customTextColor: RGBA | string | undefined, + customBackgroundColor: RGBA | string | undefined, + customUnderlineColor: RGBA | string | undefined, + highlights: IHighlight[] | undefined, + offset: number, ): void { if (!root || !stringContent) { return; } - const container = linkDetector.linkify(stringContent, true, workspaceFolder); + const container = linkDetector.linkify( + stringContent, + true, + workspaceFolder, + undefined, + undefined, + highlights?.map(h => ({ start: h.start - offset, end: h.end - offset, extraClasses: h.extraClasses })), + ); container.className = cssClasses.join(' '); if (customTextColor) { diff --git a/src/vs/workbench/contrib/debug/browser/debugExpressionRenderer.ts b/src/vs/workbench/contrib/debug/browser/debugExpressionRenderer.ts index 767950dc826..0835365d408 100644 --- a/src/vs/workbench/contrib/debug/browser/debugExpressionRenderer.ts +++ b/src/vs/workbench/contrib/debug/browser/debugExpressionRenderer.ts @@ -16,7 +16,7 @@ import { observableConfigValue } from '../../../../platform/observable/common/pl import { IDebugSession, IExpressionValue } from '../common/debug.js'; import { Expression, ExpressionContainer, Variable } from '../common/debugModel.js'; import { ReplEvaluationResult } from '../common/replModel.js'; -import { IVariableTemplateData } from './baseDebugView.js'; +import { IVariableTemplateData, splitExpressionOrScopeHighlights } from './baseDebugView.js'; import { handleANSIOutput } from './debugANSIHandling.js'; import { COPY_EVALUATE_PATH_ID, COPY_VALUE_ID } from './debugCommands.js'; import { DebugLinkHoverBehavior, DebugLinkHoverBehaviorTypeData, ILinkDetector, LinkDetector } from './linkDetector.js'; @@ -32,6 +32,7 @@ export interface IRenderValueOptions { /** If not false, a rich hover will be shown on the element. */ hover?: false | IValueHoverOptions; colorize?: boolean; + highlights?: IHighlight[]; /** * Indicates areas where VS Code implicitly always supported ANSI escape @@ -90,6 +91,7 @@ export class DebugExpressionRenderer { renderVariable(data: IVariableTemplateData, variable: Variable, options: IRenderVariableOptions = {}): IDisposable { const displayType = this.displayType.get(); + const highlights = splitExpressionOrScopeHighlights(variable, options.highlights || []); if (variable.available) { data.type.textContent = ''; @@ -103,7 +105,7 @@ export class DebugExpressionRenderer { } } - data.label.set(text, options.highlights, variable.type && !displayType ? variable.type : variable.name); + data.label.set(text, highlights.name, variable.type && !displayType ? variable.type : variable.name); data.name.classList.toggle('virtual', variable.presentationHint?.kind === 'virtual'); data.name.classList.toggle('internal', variable.presentationHint?.visibility === 'internal'); } else if (variable.value && typeof variable.name === 'string' && variable.name) { @@ -122,6 +124,7 @@ export class DebugExpressionRenderer { showChanged: options.showChanged, maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, hover: { commands }, + highlights: highlights.value, colorize: true, session: variable.getSession(), }); @@ -184,9 +187,9 @@ export class DebugExpressionRenderer { } if (supportsANSI) { - container.appendChild(handleANSIOutput(value, linkDetector, session ? session.root : undefined)); + container.appendChild(handleANSIOutput(value, linkDetector, session ? session.root : undefined, options.highlights)); } else { - container.appendChild(linkDetector.linkify(value, false, session?.root, true, hoverBehavior)); + container.appendChild(linkDetector.linkify(value, false, session?.root, true, hoverBehavior, options.highlights)); } if (options.hover !== false) { @@ -199,7 +202,7 @@ export class DebugExpressionRenderer { if (supportsANSI) { // note: intentionally using `this.linkDetector` so we don't blindly linkify the // entire contents and instead only link file paths that it contains. - hoverContentsPre.appendChild(handleANSIOutput(value, this.linkDetector, session ? session.root : undefined)); + hoverContentsPre.appendChild(handleANSIOutput(value, this.linkDetector, session ? session.root : undefined, options.highlights)); } else { hoverContentsPre.textContent = value; } diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index 98a89453a5a..8d08e6f1be9 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getWindow } from '../../../../base/browser/dom.js'; +import { getWindow, isHTMLElement, reset } from '../../../../base/browser/dom.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; @@ -23,6 +23,8 @@ import { IDebugSession } from '../common/debug.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; import { IPathService } from '../../../services/path/common/pathService.js'; +import { IHighlight } from '../../../../base/browser/ui/highlightedlabel/highlightedLabel.js'; +import { Iterable } from '../../../../base/common/iterator.js'; const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f'; const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug'); @@ -42,6 +44,7 @@ type LinkPart = { kind: LinkKind; value: string; captures: string[]; + index: number; }; export const enum DebugLinkHoverBehavior { @@ -61,7 +64,7 @@ export type DebugLinkHoverBehaviorTypeData = { type: DebugLinkHoverBehavior.None | { type: DebugLinkHoverBehavior.Rich; store: DisposableStore }; export interface ILinkDetector { - linkify(text: string, splitLines?: boolean, workspaceFolder?: IWorkspaceFolder, includeFulltext?: boolean, hoverBehavior?: DebugLinkHoverBehaviorTypeData): HTMLElement; + linkify(text: string, splitLines?: boolean, workspaceFolder?: IWorkspaceFolder, includeFulltext?: boolean, hoverBehavior?: DebugLinkHoverBehaviorTypeData, highlights?: IHighlight[]): HTMLElement; linkifyLocation(text: string, locationReference: number, session: IDebugSession, hoverBehavior?: DebugLinkHoverBehaviorTypeData): HTMLElement; } @@ -88,11 +91,11 @@ export class LinkDetector implements ILinkDetector { * If a `hoverBehavior` is passed, hovers may be added using the workbench hover service. * This should be preferred for new code where hovers are desirable. */ - linkify(text: string, splitLines?: boolean, workspaceFolder?: IWorkspaceFolder, includeFulltext?: boolean, hoverBehavior?: DebugLinkHoverBehaviorTypeData): HTMLElement { - return this._linkify(text, splitLines, workspaceFolder, includeFulltext, hoverBehavior); + linkify(text: string, splitLines?: boolean, workspaceFolder?: IWorkspaceFolder, includeFulltext?: boolean, hoverBehavior?: DebugLinkHoverBehaviorTypeData, highlights?: IHighlight[]): HTMLElement { + return this._linkify(text, splitLines, workspaceFolder, includeFulltext, hoverBehavior, highlights); } - private _linkify(text: string, splitLines?: boolean, workspaceFolder?: IWorkspaceFolder, includeFulltext?: boolean, hoverBehavior?: DebugLinkHoverBehaviorTypeData, defaultRef?: { locationReference: number; session: IDebugSession }): HTMLElement { + private _linkify(text: string, splitLines?: boolean, workspaceFolder?: IWorkspaceFolder, includeFulltext?: boolean, hoverBehavior?: DebugLinkHoverBehaviorTypeData, highlights?: IHighlight[], defaultRef?: { locationReference: number; session: IDebugSession }): HTMLElement { if (splitLines) { const lines = text.split('\n'); for (let i = 0; i < lines.length - 1; i++) { @@ -102,7 +105,7 @@ export class LinkDetector implements ILinkDetector { // Remove the last element ('') that split added. lines.pop(); } - const elements = lines.map(line => this._linkify(line, false, workspaceFolder, includeFulltext, hoverBehavior, defaultRef)); + const elements = lines.map(line => this._linkify(line, false, workspaceFolder, includeFulltext, hoverBehavior, highlights, defaultRef)); if (elements.length === 1) { // Do not wrap single line with extra span. return elements[0]; @@ -115,21 +118,26 @@ export class LinkDetector implements ILinkDetector { const container = document.createElement('span'); for (const part of this.detectLinks(text)) { try { + let node: Node; switch (part.kind) { case 'text': - container.appendChild(defaultRef ? this.linkifyLocation(part.value, defaultRef.locationReference, defaultRef.session, hoverBehavior) : document.createTextNode(part.value)); + node = defaultRef ? this.linkifyLocation(part.value, defaultRef.locationReference, defaultRef.session, hoverBehavior) : document.createTextNode(part.value); break; case 'web': - container.appendChild(this.createWebLink(includeFulltext ? text : undefined, part.value, hoverBehavior)); + node = this.createWebLink(includeFulltext ? text : undefined, part.value, hoverBehavior); break; case 'path': { const path = part.captures[0]; const lineNumber = part.captures[1] ? Number(part.captures[1]) : 0; const columnNumber = part.captures[2] ? Number(part.captures[2]) : 0; - container.appendChild(this.createPathLink(includeFulltext ? text : undefined, part.value, path, lineNumber, columnNumber, workspaceFolder, hoverBehavior)); + node = this.createPathLink(includeFulltext ? text : undefined, part.value, path, lineNumber, columnNumber, workspaceFolder, hoverBehavior); break; } + default: + node = document.createTextNode(part.value); } + + container.append(...this.applyHighlights(node, part.index, part.value.length, highlights)); } catch (e) { container.appendChild(document.createTextNode(part.value)); } @@ -137,6 +145,50 @@ export class LinkDetector implements ILinkDetector { return container; } + private applyHighlights(node: Node, startIndex: number, length: number, highlights: IHighlight[] | undefined): Iterable { + const children: (Node | string)[] = []; + let currentIndex = startIndex; + const endIndex = startIndex + length; + + for (const highlight of highlights || []) { + if (highlight.end <= currentIndex || highlight.start >= endIndex) { + continue; + } + + if (highlight.start > currentIndex) { + children.push(node.textContent!.substring(currentIndex - startIndex, highlight.start - startIndex)); + currentIndex = highlight.start; + } + + const highlightEnd = Math.min(highlight.end, endIndex); + const highlightedText = node.textContent!.substring(currentIndex - startIndex, highlightEnd - startIndex); + const highlightSpan = document.createElement('span'); + highlightSpan.classList.add('highlight'); + if (highlight.extraClasses) { + highlightSpan.classList.add(...highlight.extraClasses); + } + highlightSpan.textContent = highlightedText; + children.push(highlightSpan); + currentIndex = highlightEnd; + } + + if (currentIndex === startIndex) { + return Iterable.single(node); // no changes made + } + + if (currentIndex < endIndex) { + children.push(node.textContent!.substring(currentIndex - startIndex)); + } + + // reuse the element if it's a link + if (isHTMLElement(node)) { + reset(node, ...children); + return Iterable.single(node); + } + + return children; + } + /** * Linkifies a location reference. */ @@ -161,8 +213,8 @@ export class LinkDetector implements ILinkDetector { */ makeReferencedLinkDetector(locationReference: number, session: IDebugSession): ILinkDetector { return { - linkify: (text, splitLines, workspaceFolder, includeFulltext, hoverBehavior) => - this._linkify(text, splitLines, workspaceFolder, includeFulltext, hoverBehavior, { locationReference, session }), + linkify: (text, splitLines, workspaceFolder, includeFulltext, hoverBehavior, highlights) => + this._linkify(text, splitLines, workspaceFolder, includeFulltext, hoverBehavior, highlights, { locationReference, session }), linkifyLocation: this.linkifyLocation.bind(this), }; } @@ -295,16 +347,16 @@ export class LinkDetector implements ILinkDetector { private detectLinks(text: string): LinkPart[] { if (text.length > MAX_LENGTH) { - return [{ kind: 'text', value: text, captures: [] }]; + return [{ kind: 'text', value: text, captures: [], index: 0 }]; } const regexes: RegExp[] = [WEB_LINK_REGEX, PATH_LINK_REGEX]; const kinds: LinkKind[] = ['web', 'path']; const result: LinkPart[] = []; - const splitOne = (text: string, regexIndex: number) => { + const splitOne = (text: string, regexIndex: number, baseIndex: number) => { if (regexIndex >= regexes.length) { - result.push({ value: text, kind: 'text', captures: [] }); + result.push({ value: text, kind: 'text', captures: [], index: baseIndex }); return; } const regex = regexes[regexIndex]; @@ -314,23 +366,24 @@ export class LinkDetector implements ILinkDetector { while ((match = regex.exec(text)) !== null) { const stringBeforeMatch = text.substring(currentIndex, match.index); if (stringBeforeMatch) { - splitOne(stringBeforeMatch, regexIndex + 1); + splitOne(stringBeforeMatch, regexIndex + 1, baseIndex + currentIndex); } const value = match[0]; result.push({ value: value, kind: kinds[regexIndex], - captures: match.slice(1) + captures: match.slice(1), + index: baseIndex + match.index }); currentIndex = match.index + value.length; } const stringAfterMatches = text.substring(currentIndex); if (stringAfterMatches) { - splitOne(stringAfterMatches, regexIndex + 1); + splitOne(stringAfterMatches, regexIndex + 1, baseIndex + currentIndex); } }; - splitOne(text, 0); + splitOne(text, 0, 0); return result; } } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index 90dfb51bc60..f795c6c22eb 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -274,6 +274,12 @@ font-family: var(--monaco-monospace-font); font-weight: normal; } +.debug-view-content .monaco-tl-contents .highlight { + color: unset !important; + background-color: var(--vscode-list-filterMatchBackground); + outline: 1px dotted var(--vscode-list-filterMatchBorder); + outline-offset: -1px; +} /* Breakpoints */ diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 16d53c4f87e..74c22d9ee97 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -46,7 +46,7 @@ import { CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS import { getContextForVariable } from '../common/debugContext.js'; import { ErrorScope, Expression, Scope, StackFrame, Variable, VisualizedExpression, getUriForDebugMemory } from '../common/debugModel.js'; import { DebugVisualizer, IDebugVisualizerService } from '../common/debugVisualizers.js'; -import { AbstractExpressionDataSource, AbstractExpressionsRenderer, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js'; +import { AbstractExpressionDataSource, AbstractExpressionsRenderer, expressionAndScopeLabelProvider, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js'; import { ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, COPY_VALUE_ID, COPY_VALUE_LABEL } from './debugCommands.js'; import { DebugExpressionRenderer } from './debugExpressionRenderer.js'; @@ -138,7 +138,7 @@ export class VariablesView extends ViewPane implements IDebugViewWithVariables { this.instantiationService.createInstance(VariablesDataSource), { accessibilityProvider: new VariablesAccessibilityProvider(), identityProvider: { getId: (element: IExpression | IScope) => element.getId() }, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IExpression | IScope) => e.name }, + keyboardNavigationLabelProvider: expressionAndScopeLabelProvider, overrideStyles: this.getLocationBasedColors().listOverrideStyles }); diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index b325c138b0a..a56d4aae462 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -31,7 +31,7 @@ import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.j import { IViewDescriptorService } from '../../../common/views.js'; import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IDebugViewWithVariables, IExpression, WATCH_VIEW_ID } from '../common/debug.js'; import { Expression, Variable, VisualizedExpression } from '../common/debugModel.js'; -import { AbstractExpressionDataSource, AbstractExpressionsRenderer, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js'; +import { AbstractExpressionDataSource, AbstractExpressionsRenderer, expressionAndScopeLabelProvider, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js'; import { DebugExpressionRenderer } from './debugExpressionRenderer.js'; import { watchExpressionsAdd, watchExpressionsRemoveAll } from './debugIcons.js'; import { VariablesRenderer, VisualizedVariableRenderer } from './variablesView.js'; @@ -109,7 +109,7 @@ export class WatchExpressionsView extends ViewPane implements IDebugViewWithVari return undefined; } - return e.name; + return expressionAndScopeLabelProvider.getKeyboardNavigationLabel(e); } }, dnd: new WatchExpressionsDragAndDrop(this.debugService), diff --git a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts index eb31a388b2e..17d23dc6303 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts @@ -51,8 +51,8 @@ suite('Debug - ANSI Handling', () => { assert.strictEqual(0, root.children.length); - appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session.root); - appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session.root); + appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session.root, undefined, undefined, undefined, undefined, 0); + appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session.root, undefined, undefined, undefined, undefined, 0); assert.strictEqual(2, root.children.length); @@ -82,7 +82,7 @@ suite('Debug - ANSI Handling', () => { * @returns An {@link HTMLSpanElement} that contains the stylized text. */ function getSequenceOutput(sequence: string): HTMLSpanElement { - const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root); + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root, []); assert.strictEqual(1, root.children.length); const child: Node = root.lastChild!; if (isHTMLSpanElement(child)) { @@ -395,7 +395,7 @@ suite('Debug - ANSI Handling', () => { if (elementsExpected === undefined) { elementsExpected = assertions.length; } - const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root); + const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root, []); assert.strictEqual(elementsExpected, root.children.length); for (let i = 0; i < elementsExpected; i++) { const child: Node = root.children[i]; diff --git a/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts b/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts index c078c4a371e..9e2d0c8c767 100644 --- a/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts @@ -13,6 +13,7 @@ import { ITunnelService } from '../../../../../platform/tunnel/common/tunnel.js' import { WorkspaceFolder } from '../../../../../platform/workspace/common/workspace.js'; import { LinkDetector } from '../../browser/linkDetector.js'; import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js'; +import { IHighlight } from '../../../../../base/browser/ui/highlightedlabel/highlightedLabel.js'; suite('Debug - Link Detector', () => { @@ -168,4 +169,67 @@ suite('Debug - Link Detector', () => { assertElementIsLink(output.children[1].children[0]); assert.strictEqual(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].children[0].textContent); }); + + test('highlightNoLinks', () => { + const input = 'I am a string'; + const highlights: IHighlight[] = [{ start: 2, end: 5 }]; + const expectedOutput = 'I am a string'; + const output = linkDetector.linkify(input, false, undefined, false, undefined, highlights); + + assert.strictEqual(1, output.children.length); + assert.strictEqual('SPAN', output.tagName); + assert.strictEqual(expectedOutput, output.outerHTML); + }); + + test('highlightWithLink', () => { + const input = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const highlights: IHighlight[] = [{ start: 0, end: 5 }]; + const expectedOutput = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const output = linkDetector.linkify(input, false, undefined, false, undefined, highlights); + + assert.strictEqual(1, output.children.length); + assert.strictEqual('SPAN', output.tagName); + assert.strictEqual('A', output.firstElementChild!.tagName); + assert.strictEqual(expectedOutput, output.outerHTML); + assertElementIsLink(output.firstElementChild!); + }); + + test('highlightOverlappingLinkStart', () => { + const input = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const highlights: IHighlight[] = [{ start: 0, end: 10 }]; + const expectedOutput = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const output = linkDetector.linkify(input, false, undefined, false, undefined, highlights); + + assert.strictEqual(1, output.children.length); + assert.strictEqual('SPAN', output.tagName); + assert.strictEqual('A', output.firstElementChild!.tagName); + assert.strictEqual(expectedOutput, output.outerHTML); + assertElementIsLink(output.firstElementChild!); + }); + + test('highlightOverlappingLinkEnd', () => { + const input = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const highlights: IHighlight[] = [{ start: 10, end: 20 }]; + const expectedOutput = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const output = linkDetector.linkify(input, false, undefined, false, undefined, highlights); + + assert.strictEqual(1, output.children.length); + assert.strictEqual('SPAN', output.tagName); + assert.strictEqual('A', output.firstElementChild!.tagName); + assert.strictEqual(expectedOutput, output.outerHTML); + assertElementIsLink(output.firstElementChild!); + }); + + test('highlightOverlappingLinkStartAndEnd', () => { + const input = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const highlights: IHighlight[] = [{ start: 5, end: 15 }]; + const expectedOutput = isWindows ? 'C:\\foo\\bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const output = linkDetector.linkify(input, false, undefined, false, undefined, highlights); + + assert.strictEqual(1, output.children.length); + assert.strictEqual('SPAN', output.tagName); + assert.strictEqual('A', output.firstElementChild!.tagName); + assert.strictEqual(expectedOutput, output.outerHTML); + assertElementIsLink(output.firstElementChild!); + }); }); From 151ef3514e76629f4e7bf3951439b1e0dae0a6e5 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Sun, 22 Dec 2024 14:09:59 +0100 Subject: [PATCH 466/479] SCM - disable actions for resource groups that do not have any resources (#236813) --- extensions/git/package.json | 14 ++++++------- src/vs/workbench/contrib/scm/browser/menus.ts | 20 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 53aa0fad747..4556255d3da 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -163,14 +163,14 @@ "title": "%command.stageAll%", "category": "Git", "icon": "$(add)", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmResourceGroupResourceCount > 0" }, { "command": "git.stageAllTracked", "title": "%command.stageAllTracked%", "category": "Git", "icon": "$(add)", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmResourceGroupResourceCount > 0" }, { "command": "git.stageAllUntracked", @@ -243,7 +243,7 @@ "title": "%command.unstageAll%", "category": "Git", "icon": "$(remove)", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmResourceGroupResourceCount > 0" }, { "command": "git.unstageSelectedRanges", @@ -270,14 +270,14 @@ "title": "%command.cleanAll%", "category": "Git", "icon": "$(discard)", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmResourceGroupResourceCount > 0" }, { "command": "git.cleanAllTracked", "title": "%command.cleanAllTracked%", "category": "Git", "icon": "$(discard)", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmResourceGroupResourceCount > 0" }, { "command": "git.cleanAllUntracked", @@ -889,14 +889,14 @@ "title": "%command.viewChanges%", "icon": "$(diff-multiple)", "category": "Git", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmResourceGroupResourceCount > 0" }, { "command": "git.viewStagedChanges", "title": "%command.viewStagedChanges%", "icon": "$(diff-multiple)", "category": "Git", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmResourceGroupResourceCount > 0" }, { "command": "git.viewUntrackedChanges", diff --git a/src/vs/workbench/contrib/scm/browser/menus.ts b/src/vs/workbench/contrib/scm/browser/menus.ts index f14d8ea79f6..352f63b6f3e 100644 --- a/src/vs/workbench/contrib/scm/browser/menus.ts +++ b/src/vs/workbench/contrib/scm/browser/menus.ts @@ -6,7 +6,7 @@ import { IAction } from '../../../../base/common/actions.js'; import { equals } from '../../../../base/common/arrays.js'; import { Emitter } from '../../../../base/common/event.js'; -import { DisposableStore, IDisposable, dispose } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, IDisposable, MutableDisposable, dispose } from '../../../../base/common/lifecycle.js'; import './media/scm.css'; import { localize } from '../../../../nls.js'; import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; @@ -70,13 +70,14 @@ interface IContextualResourceMenuItem { class SCMMenusItem implements IDisposable { - private _resourceGroupMenu: IMenu | undefined; + private readonly _resourceGroupMenu = new MutableDisposable(); get resourceGroupMenu(): IMenu { - if (!this._resourceGroupMenu) { - this._resourceGroupMenu = this.menuService.createMenu(MenuId.SCMResourceGroupContext, this.contextKeyService); - } + const contextKeyService = this.contextKeyService.createOverlay([ + ['scmResourceGroupResourceCount', this.group.resources.length], + ]); - return this._resourceGroupMenu; + this._resourceGroupMenu.value = this.menuService.createMenu(MenuId.SCMResourceGroupContext, contextKeyService); + return this._resourceGroupMenu.value; } private _resourceFolderMenu: IMenu | undefined; @@ -92,8 +93,9 @@ class SCMMenusItem implements IDisposable { private contextualResourceMenus: Map | undefined; constructor( - private contextKeyService: IContextKeyService, - private menuService: IMenuService + private readonly group: ISCMResourceGroup, + private readonly contextKeyService: IContextKeyService, + private readonly menuService: IMenuService ) { } getResourceMenu(resource: ISCMResource): IMenu { @@ -206,7 +208,7 @@ export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable { ['multiDiffEditorEnableViewChanges', group.multiDiffEditorEnableViewChanges], ]); - result = new SCMMenusItem(contextKeyService, this.menuService); + result = new SCMMenusItem(group, contextKeyService, this.menuService); this.resourceGroupMenusItems.set(group, result); } From 2e5cbd49c8252d6e1dc44de2ce8f63899a8774f7 Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 23 Dec 2024 19:22:49 +0900 Subject: [PATCH 467/479] chore: update electron@32.2.7 (#236843) * chore: update electron@32.2.7 * chore: bump distro --- .npmrc | 4 +- build/checksums/electron.txt | 150 +++++++++++++++++------------------ cgmanifest.json | 4 +- package-lock.json | 8 +- package.json | 4 +- 5 files changed, 85 insertions(+), 85 deletions(-) diff --git a/.npmrc b/.npmrc index 22256e5d8e7..27692c2409e 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,6 @@ disturl="https://electronjs.org/headers" -target="32.2.6" -ms_build_id="10629634" +target="32.2.7" +ms_build_id="10660205" runtime="electron" build_from_source="true" legacy-peer-deps="true" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index 17ec96faa2a..293250496af 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -bb4164f7b554606b2c4daaf43e81bf2e2b5cf0d4441cfdd74f04653237fcf655 *chromedriver-v32.2.6-darwin-arm64.zip -a0fc3df1c6cd17bfe62ffbb1eba3655ca625dea5046e5d2b3dbb0e9e349cd10e *chromedriver-v32.2.6-darwin-x64.zip -671d6dab890747ea73ba5589327eef7612670950a20e5f88c7d8a301b5491e26 *chromedriver-v32.2.6-linux-arm64.zip -55bfd4e33fef1506261d4cb3074988e1970c2a762ca76a8f1197512a1766723c *chromedriver-v32.2.6-linux-armv7l.zip -d3c7a45c8c75152db927b3596f506995e72631df870b302b7dbcbd3399e54a3a *chromedriver-v32.2.6-linux-x64.zip -567f77d09708942901c6cdce6708b995f6ac779faceebb4ed383ca5003e2de4e *chromedriver-v32.2.6-mas-arm64.zip -b3a28181b1d077742f1be632a802e15b5a36a260b1cfe0e429735de9f52d074a *chromedriver-v32.2.6-mas-x64.zip -a113f5bd747b6eeb033f4d6ea2f53cf332d9b45d6340af514dd938bac7f99419 *chromedriver-v32.2.6-win32-arm64.zip -3b3237a788fad0a6be63a69b93c28b6052db23aeaa1a75d2589be15b4c2c0f2f *chromedriver-v32.2.6-win32-ia32.zip -1096c131cf8e3f98a01525e93d573eaf4fd23492d8dd78a211e39c448e69e463 *chromedriver-v32.2.6-win32-x64.zip -8e6fcf3171c3fcdcb117f641ec968bb53be3d38696e388636bf34f04c10b987d *electron-api.json -b1b20784a97e64992c92480e69af828a110d834372479b26759f1559b3da80fc *electron-v32.2.6-darwin-arm64-dsym-snapshot.zip -f11dd5a84229430ec59b4335415a4b308dc4330ff7b9febae20165fbdd862e92 *electron-v32.2.6-darwin-arm64-dsym.zip -cc96cf91f6b108dc927d8f7daee2fe27ae8a492c932993051508aa779e816445 *electron-v32.2.6-darwin-arm64-symbols.zip -fcb6bbb6aa3c1020b4045dbe9f2a5286173d5025248550f55631e70568e91775 *electron-v32.2.6-darwin-arm64.zip -d2bfeea27fc91936b4f71d0f5c577e5ad0ea094edba541dfa348948fd65c3331 *electron-v32.2.6-darwin-x64-dsym-snapshot.zip -5e7684cc12c0dee11fb933b68301d0fe68d3198d1daeadd5e1b4cf52743f79bf *electron-v32.2.6-darwin-x64-dsym.zip -6d2a7d41ab14fc7d3c5e4b35d5d425edb2d13978dcc332e781ec8b7bcfe6a794 *electron-v32.2.6-darwin-x64-symbols.zip -c0964ee5fdcefb1003ffd7ef574b07e5147856f3a94bb4335f78c409f8cf2eca *electron-v32.2.6-darwin-x64.zip -604d88b9d434ea66ddf234dd129dcef3d468b95b0da47e2f1555a682c8d0de28 *electron-v32.2.6-linux-arm64-debug.zip -f448e91df42fc84177bcd46378e49ee648f6114984fc57af4a84690a5197c23e *electron-v32.2.6-linux-arm64-symbols.zip -9e4f9345cae06e8e5679b228e7b7ac21b8733e3fcda8903e3dcbc8171c53f8be *electron-v32.2.6-linux-arm64.zip -604d88b9d434ea66ddf234dd129dcef3d468b95b0da47e2f1555a682c8d0de28 *electron-v32.2.6-linux-armv7l-debug.zip -c85d5ca3f38dc4140040bcde6a37ac9c7510bb542f12e1ffce695a35f68e3c13 *electron-v32.2.6-linux-armv7l-symbols.zip -df4b490a9c501d83c5305f20b2a9d1aa100d2e878e59ebafde477f21d35e3300 *electron-v32.2.6-linux-armv7l.zip -a5aa67da85ee318ff0770d55a506f62e6e5a10e967dfab272a94bcd91922e075 *electron-v32.2.6-linux-x64-debug.zip -671eb342a58e056f0dee5a6e9c69a6a96ee2141f81d306fa1a0e2635e22aa7c0 *electron-v32.2.6-linux-x64-symbols.zip -a3231409db7f8ac2cc446708f17e2abac0f8549c166eaab2f427e8d0f864208d *electron-v32.2.6-linux-x64.zip -8b8d0aeadcf21633216a9cce87d323ad6aa21e38ec82092cd5f65bf171be025f *electron-v32.2.6-mas-arm64-dsym-snapshot.zip -298931236955b83d174738d3325931e9672a8333bf854fc5f471168b0f7e70be *electron-v32.2.6-mas-arm64-dsym.zip -53e4c666a6f5f87aa150b1c2f34532e3711e00b0237fb103dcbef64d65979429 *electron-v32.2.6-mas-arm64-symbols.zip -bedbc78acc3bc6cb30e5fe1f133562104152022273ec21a83cd32a0eece9003f *electron-v32.2.6-mas-arm64.zip -9ba3def756c90460867d968af5417996991bf3b4b306dba4c9fbde43de2f771d *electron-v32.2.6-mas-x64-dsym-snapshot.zip -e4a3f9392934bb4ef82a214fec2ce1f319ea409b89bf03b2a2a4ab7a55688d0c *electron-v32.2.6-mas-x64-dsym.zip -55c54fd01faf5767493183001a440b837b63f8b8d64c36f7b42fa7217af36dcd *electron-v32.2.6-mas-x64-symbols.zip -1efe974cd426a288d617750c864e6edbf28495c3b851a5bc806af19cda7a274d *electron-v32.2.6-mas-x64.zip -88c50d34dc48a55a11014349d2d278f895f1615405614dbfcf27dff5f5030cf1 *electron-v32.2.6-win32-arm64-pdb.zip -b0b2211bf0f11924bcd693b6783627c7f6c9a066117bcf05c10766064c79794c *electron-v32.2.6-win32-arm64-symbols.zip -48b81d28fdceb4ab3ca27650d79bab910a1a19dbda72271882bfdc877c71975f *electron-v32.2.6-win32-arm64-toolchain-profile.zip -2b7962348f23410863cb6562d79654ce534666bab9f75965b5c8ebee61f49657 *electron-v32.2.6-win32-arm64.zip -d248ab4ec8b4a5aec414c7a404e0efd9b9a73083f7c5cb6c657c2ed58c4cbe94 *electron-v32.2.6-win32-ia32-pdb.zip -2ef98197d66d94aee4978047f22ba07538d259b25a8f5f301d9564a13649e72c *electron-v32.2.6-win32-ia32-symbols.zip -48b81d28fdceb4ab3ca27650d79bab910a1a19dbda72271882bfdc877c71975f *electron-v32.2.6-win32-ia32-toolchain-profile.zip -e85dbf85d58cab5b06cdb8e76fde3a25306e7c1808f5bb30925ba7e29ff14d13 *electron-v32.2.6-win32-ia32.zip -9d76b0c0d475cc062b2a951fbfb3b17837880102fb6cc042e2736dfebbfa0e5d *electron-v32.2.6-win32-x64-pdb.zip -3d7b93bafc633429f5c9f820bcb4843d504b12e454b3ecebb1b69c15b5f4080f *electron-v32.2.6-win32-x64-symbols.zip -48b81d28fdceb4ab3ca27650d79bab910a1a19dbda72271882bfdc877c71975f *electron-v32.2.6-win32-x64-toolchain-profile.zip -77d5e5b76b49767e6a3ad292dc315fbc7cdccd557ac38da9093b8ac6da9262d5 *electron-v32.2.6-win32-x64.zip -a52935712eb4fc2c12ac4ae611a57e121da3b6165c2de1abd7392ed4261287e2 *electron.d.ts -6345ea55fda07544434c90c276cdceb2662044b9e0355894db67ca95869af22a *ffmpeg-v32.2.6-darwin-arm64.zip -4c1347e8653727513a22be013008c2760d19200977295b98506b3b9947e74090 *ffmpeg-v32.2.6-darwin-x64.zip -3f1eafaf4cd90ab43ba0267429189be182435849a166a2cbe1faefc0d07217c4 *ffmpeg-v32.2.6-linux-arm64.zip -3db919bc57e1a5bf7c1bae1d7aeacf4a331990ea82750391c0b24a046d9a2812 *ffmpeg-v32.2.6-linux-armv7l.zip -fe7d779dddbfb5da5999a7607fc5e3c7a6ab7c65e8da9fee1384918865231612 *ffmpeg-v32.2.6-linux-x64.zip -e09ae881113d1b3103aec918e7c95c36f82b2db63657320c380c94386f689138 *ffmpeg-v32.2.6-mas-arm64.zip -ee316e435662201a81fcededc62582dc87a0bd5c9fd0f6a8a55235eca806652f *ffmpeg-v32.2.6-mas-x64.zip -415395968d31e13056cefcb589ed550a0e80d7c3d0851ee3ba29e4dcdf174210 *ffmpeg-v32.2.6-win32-arm64.zip -17f61a5293b707c984cee52b57a7e505cde994d22828e31c016434521638e419 *ffmpeg-v32.2.6-win32-ia32.zip -f9b47951a553eec21636b3cc15eccf0a2ba272567146ec8a6e2eeb91a985fd62 *ffmpeg-v32.2.6-win32-x64.zip -7d2b596bd94e4d5c7befba11662dc563a02f18c183da12ebd56f38bb6f4382e9 *hunspell_dictionaries.zip -06bca9a33142b5834fbca6d10d7f3303b0b5c52e7222fe783db109cd4c5260ed *libcxx-objects-v32.2.6-linux-arm64.zip -7ab8ff5a4e1d3a6639978ed718d2732df9c1a4dd4dcd3e18f6746a2168d353a9 *libcxx-objects-v32.2.6-linux-armv7l.zip -ba96792896751e11fdba63e5e336e323979986176aa1848122ca3757c854352e *libcxx-objects-v32.2.6-linux-x64.zip -1f858d484f4ce27f9f3c4a120b2881f88c17c81129ca10e495b50398fb2eed64 *libcxx_headers.zip -4566afb06a6dd8bd895dba5350a5705868203321116a1ae0a216d5a4a6bfb289 *libcxxabi_headers.zip -350f5419c14aede5802c4f0ef5204ddbbe0eb7d5263524da38d19f43620d8530 *mksnapshot-v32.2.6-darwin-arm64.zip -9f4e4943df4502943994ffa17525b7b5b9a1d889dbc5aeb28bfc40f9146323ec *mksnapshot-v32.2.6-darwin-x64.zip -6295ad1a4ab3b24ac99ec85d35ebfce3861e58185b94d20077e364e81ad935f8 *mksnapshot-v32.2.6-linux-arm64-x64.zip -ed9e1931165a2ff85c1af9f10b3bf8ba05d2dae31331d1d4f5ff1512078e4411 *mksnapshot-v32.2.6-linux-armv7l-x64.zip -6680c63b11e4638708d88c130474ceb2ee92fc58c62cfcd3bf33dda9fee771c2 *mksnapshot-v32.2.6-linux-x64.zip -5a4c45b755b7bbdcad51345f5eb2935ba988987650f9b8540c7ef04015207a2f *mksnapshot-v32.2.6-mas-arm64.zip -1e6243d6a1b68f327457b9dae244ffaeadc265b07a99d9c36f1abcff31e36856 *mksnapshot-v32.2.6-mas-x64.zip -407099537b17860ce7dcea6ba582a854314c29dc152a6df0abcc77ebb0187b4c *mksnapshot-v32.2.6-win32-arm64-x64.zip -f9107536378e19ae9305386dacf6fae924c7ddaad0cf0a6f49694e1e8af6ded5 *mksnapshot-v32.2.6-win32-ia32.zip -82a142db76a2cc5dfb9a89e4cd721cfebc0f3077d2415d8f3d86bb7411f0fe27 *mksnapshot-v32.2.6-win32-x64.zip +0729d2cb830425c4591b40d7189c2f7da020f5adb887a49a4faa022d551b1e6f *chromedriver-v32.2.7-darwin-arm64.zip +a7d61c68d3b3522c0ec383915b6ab3d9f269d9ada0e09aa87a4e7d9fc24fe928 *chromedriver-v32.2.7-darwin-x64.zip +45314c8c7127f6083469c2c067aa78beb20055ba4d7a63eb2108b27e594a647a *chromedriver-v32.2.7-linux-arm64.zip +7e976a7131dcfd55f781c073ae59c8a24a1393119d831fbac13c6a335eb71467 *chromedriver-v32.2.7-linux-armv7l.zip +3437feb5d8e7157476d2e7a6558346061cd7e46506874bc7870eed8a3a43642a *chromedriver-v32.2.7-linux-x64.zip +3737301add80a936374acb17b84bb3a715fab9fbce049816ea7a51fa53d3b35e *chromedriver-v32.2.7-mas-arm64.zip +8b8b62f48a5e8b8a340b47348a2cc5dd4ba38789f76bc5567c039587538081a9 *chromedriver-v32.2.7-mas-x64.zip +24b666e3ab41eb1c66ab0f2361af0529b2b8e1e5ef153cfcef36adc700f9ed6d *chromedriver-v32.2.7-win32-arm64.zip +6d40251661afb1835adbef85e317fd520c0f1378156614c82befb348c3122c2d *chromedriver-v32.2.7-win32-ia32.zip +483012da9903d8d75e5e251a3262667c9a0a012a492b93dbe1237c7827eba778 *chromedriver-v32.2.7-win32-x64.zip +1e9b2b9011f56fa26f4d9fa57254ef1d0bdb34405a9bdf83a652f6258347e46d *electron-api.json +c0ea4a21f2e7e946300bf587a4e31f72ef996c497eaa94e6b8f788b917b896e5 *electron-v32.2.7-darwin-arm64-dsym-snapshot.zip +1e6e84e56cfb3a2473cab41577c160d3afcbda8337dda17c5295da90266433c9 *electron-v32.2.7-darwin-arm64-dsym.zip +9f460100fb71ef098bec26e9a09978ec1b1663165d6a358bfc398f5548a844c3 *electron-v32.2.7-darwin-arm64-symbols.zip +71e76a0a81a0c1c10e9e4862caf96437ba85a18c8fa7d8e15d59e3c057b893bd *electron-v32.2.7-darwin-arm64.zip +e406d690365f332826843c86c6a1b5c0320a84b0527ad8700a0e995b12a35f8c *electron-v32.2.7-darwin-x64-dsym-snapshot.zip +53d6fb64d717af80f024284161a432aaffb47631ef7548f18f33016c3376871a *electron-v32.2.7-darwin-x64-dsym.zip +3c9187db2cc0570d7b01651fa78294df7d451c87c361335cee80a61c1c561b67 *electron-v32.2.7-darwin-x64-symbols.zip +34310ed51d32b6c02ba3e3f447b0807ea85804d1f2b239e02a9de58b9080fbf8 *electron-v32.2.7-darwin-x64.zip +e884f2f9f3d001488888929b8affe053a60a7a780af7d0ec8d7023023f40ca52 *electron-v32.2.7-linux-arm64-debug.zip +fd88e47e7b564b006f68641b5c328721bbc8d87cfc9e569d9733354d263cddee *electron-v32.2.7-linux-arm64-symbols.zip +fb6e1f24385c3058844bd768320d5b332b4cbd011ab930e7252dc330c8ee17b3 *electron-v32.2.7-linux-arm64.zip +e884f2f9f3d001488888929b8affe053a60a7a780af7d0ec8d7023023f40ca52 *electron-v32.2.7-linux-armv7l-debug.zip +f338ea7ea592c3ccdad1bb788e9b936610f825ac69290e48d394790d5266dce3 *electron-v32.2.7-linux-armv7l-symbols.zip +4a95643e88cadfb011354d25cafe242cdb8c5327d65f86b4fbabe64717367ed2 *electron-v32.2.7-linux-armv7l.zip +e6e0fce9f6d95a84653b537b741967cae48c4c70c8026c02293c979074225b46 *electron-v32.2.7-linux-x64-debug.zip +122cc565d0ccd2774e298645473869752d27d2632aa97583d93b499e9b02f22b *electron-v32.2.7-linux-x64-symbols.zip +98007545e1d3700b32de5cb5eebcc10b9d105fb0dad6396155fdab1b40abb638 *electron-v32.2.7-linux-x64.zip +556d9ca239ee1206c9d67affa836ebb651db88eea6bee48cb7b43fa75851c72d *electron-v32.2.7-mas-arm64-dsym-snapshot.zip +662a3742b94fcbf7ab91a7c20e1430825ae7852e915fcb558d6357a310d631c6 *electron-v32.2.7-mas-arm64-dsym.zip +edd0763ead7ffd5bf5072539e5ca0be9252b9590e674e6e44e69b2057c329d79 *electron-v32.2.7-mas-arm64-symbols.zip +a4483f5246ecadfa48b1fc671d92b5dfbc09fbd88fe386f2ce48f10de79f2127 *electron-v32.2.7-mas-arm64.zip +a9aad4c413d4851fa3463eeef7015e3a3e77a501192965db1c5b870fa31a9660 *electron-v32.2.7-mas-x64-dsym-snapshot.zip +96c20e5c4b73febd3458679e9cc939f5f8255a327b06f49188ab2e3fe8311ea3 *electron-v32.2.7-mas-x64-dsym.zip +6ac844957373114e04411d3af1cb6507e35174d1dc279cce41cb92bbf2ea5d26 *electron-v32.2.7-mas-x64-symbols.zip +888b830b991dab6cf2c4351e112a48f24a4748efefcd763d693a79161199e65a *electron-v32.2.7-mas-x64.zip +27759db6bcdd16d4ff5548684361ba4372d885d3142bf02db59837c3634b1934 *electron-v32.2.7-win32-arm64-pdb.zip +6019e6ec58e9b6da335f20874efebc42d034a179163180b3b6faedf2963ae577 *electron-v32.2.7-win32-arm64-symbols.zip +48b81d28fdceb4ab3ca27650d79bab910a1a19dbda72271882bfdc877c71975f *electron-v32.2.7-win32-arm64-toolchain-profile.zip +2c755fdd4f9fda618b2db6b8c7210c5f3106a88b1e87b83e8433b4ab4a628cc2 *electron-v32.2.7-win32-arm64.zip +4dce0b21d1c2093cc4f7c0eaf9453a38377e0076d811da3c7391f105fc1d6afb *electron-v32.2.7-win32-ia32-pdb.zip +9a0a9c3746cd40ddc9c926755633b16676714e2138d7a2d888f658a26f617039 *electron-v32.2.7-win32-ia32-symbols.zip +48b81d28fdceb4ab3ca27650d79bab910a1a19dbda72271882bfdc877c71975f *electron-v32.2.7-win32-ia32-toolchain-profile.zip +6c338c5cd0b0587349ab0f119ca8f7d2728b1c3a43fe241741087f5fdf139c9c *electron-v32.2.7-win32-ia32.zip +fa240d324c5376aa12ed2aef26597764d9bfc2fdd0d16d7f76afc2c3e3c65a29 *electron-v32.2.7-win32-x64-pdb.zip +f645b53771cbcdfaa041d9cf9581348821d82c1b185ddb913759e2d62ee2410a *electron-v32.2.7-win32-x64-symbols.zip +48b81d28fdceb4ab3ca27650d79bab910a1a19dbda72271882bfdc877c71975f *electron-v32.2.7-win32-x64-toolchain-profile.zip +819ab19b7111dfd39dff506b3cb5cd2e1d8f4bb17f96ba74b987b2eac14b6c63 *electron-v32.2.7-win32-x64.zip +ce41b10c28bd43249cd3b409e081b1c83a2b691381bdd2e3bf208ec40ca176b8 *electron.d.ts +d2491071a641ce2e0f63c1f52e3a412856dd83ca17d021af1166d6e5b4de5638 *ffmpeg-v32.2.7-darwin-arm64.zip +5c5589b2c93f834e595eb692aa768b934245d2631df69bc4cad3a6602bba0e67 *ffmpeg-v32.2.7-darwin-x64.zip +3f1eafaf4cd90ab43ba0267429189be182435849a166a2cbe1faefc0d07217c4 *ffmpeg-v32.2.7-linux-arm64.zip +3db919bc57e1a5bf7c1bae1d7aeacf4a331990ea82750391c0b24a046d9a2812 *ffmpeg-v32.2.7-linux-armv7l.zip +fe7d779dddbfb5da5999a7607fc5e3c7a6ab7c65e8da9fee1384918865231612 *ffmpeg-v32.2.7-linux-x64.zip +feeef1ab10543c813f730cc7a482b43eda35d40f1285b950e1a6d7805db2332a *ffmpeg-v32.2.7-mas-arm64.zip +96ef45180589c854fedf2d0601a20e70a65220c0820c45d0dfd4ec64724c58e0 *ffmpeg-v32.2.7-mas-x64.zip +ab4ab9cd62e40c4d3064004caa9de680cb72d8180d4facc1be06bdc886c23410 *ffmpeg-v32.2.7-win32-arm64.zip +90b5e2ebd4ff683eda97cc43ebbdee9b133b27edd2a34ae7ef37e7969d1d68be *ffmpeg-v32.2.7-win32-ia32.zip +8452085c0a650035f30a4b76e2ce1791f9b392ea7262109d29f7fe383fc41ddb *ffmpeg-v32.2.7-win32-x64.zip +78b415ebb9040dacabb6eb776a8d4837dda9a9b1ec9d64ee15db28dbb8598862 *hunspell_dictionaries.zip +a30057c37e6be5732944084575a2278616297242ae51bd474c683263cbc0c3e4 *libcxx-objects-v32.2.7-linux-arm64.zip +f9e9d1ff1a03a3e609ab8e727b1f89e77934509a4afdb849698b70e701c2176f *libcxx-objects-v32.2.7-linux-armv7l.zip +bb66e3b48f8e0706126b2b8b08827a4adda6f56c509eae4d136fcffd5414c353 *libcxx-objects-v32.2.7-linux-x64.zip +5181518d7da83fea5d8b033ab4fb7ed300f73bd8d20b8c26b624128233bd6ab2 *libcxx_headers.zip +6030ad099859b62cbdd9021b2cdb453a744a2751cb1dab30519e3e8708ad72d6 *libcxxabi_headers.zip +d3dcc4925a6bd55bc305fd41805ffee77dc8821730ac75cf4ee9ed2ca4ebdccb *mksnapshot-v32.2.7-darwin-arm64.zip +e6dfad3c30f4f38509b2fc972dd05cef06142c4832d931edba19742e06161279 *mksnapshot-v32.2.7-darwin-x64.zip +25ba5be47a721700f16af10945e71408ed86ffd6800b5d5ef04d38c0d77aa446 *mksnapshot-v32.2.7-linux-arm64-x64.zip +f7e8b50691712206587d81844bd63271f2dd49253c946a5b66bd6f169ccf94d6 *mksnapshot-v32.2.7-linux-armv7l-x64.zip +a0b119abe93c0231601b6c699cce4b78e89def766c24f9a8a06cfab3feca8f6c *mksnapshot-v32.2.7-linux-x64.zip +e3e8a496a1eaf6c8ce623fa4b139e5458cf3ce3702ea3560cded839087b60792 *mksnapshot-v32.2.7-mas-arm64.zip +c03219273c82022c29e277d07ce1d0980d25c22d39269fa3eef9547f57ec410b *mksnapshot-v32.2.7-mas-x64.zip +7684cb9c6f621db05b6e68080fade81f46d0ff8eeac94080bd635f035069d13e *mksnapshot-v32.2.7-win32-arm64-x64.zip +f7ca1d557e3d0f878b13f57dc0e00932f7a97f3dd0f0cc3bbbd565a06718bd17 *mksnapshot-v32.2.7-win32-ia32.zip +d9d8dd33561eb648e5ebd00f99418122d9a915ec63fe967e7cb0ff64ef8ee199 *mksnapshot-v32.2.7-win32-x64.zip diff --git a/cgmanifest.json b/cgmanifest.json index 832a3f604b7..1795c44c937 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "10e0ce069260b2cc984c301d5a7ecb3492f0c2f0" + "commitHash": "3007f859dad930ae80bafffc6042a146a45e4e4d" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "32.2.6" + "version": "32.2.7" }, { "component": { diff --git a/package-lock.json b/package-lock.json index 5f2b14c3e39..23a24c728bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -95,7 +95,7 @@ "cssnano": "^6.0.3", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "32.2.6", + "electron": "32.2.7", "eslint": "^9.11.1", "eslint-formatter-compact": "^8.40.0", "eslint-plugin-header": "3.1.1", @@ -6103,9 +6103,9 @@ "dev": true }, "node_modules/electron": { - "version": "32.2.6", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.2.6.tgz", - "integrity": "sha512-aGG1MLvWCf+ECUFBCmaCF52F8312OPAJfph2D0FSsFmlbfnJuNevZCbty2lFzsiIMtU7/QRo6d0ksbgR4s7y3w==", + "version": "32.2.7", + "resolved": "https://registry.npmjs.org/electron/-/electron-32.2.7.tgz", + "integrity": "sha512-y8jbQRG3xogF70XPlk5c+dWe5iRfUBo28o2NMpKd/CcW7ENIaWtBlGima8/8nmRdAaYTy1+yIt6KB0Lon9H8cA==", "dev": true, "hasInstallScript": true, "license": "MIT", diff --git a/package.json b/package.json index 3316ae225ea..ba5004eed3e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.97.0", - "distro": "45ffe59ad7c51acf28b541a2aa76cc73437ff118", + "distro": "baf3347105f6082ae1df942fd1c052cd06f7a7f0", "author": { "name": "Microsoft Corporation" }, @@ -153,7 +153,7 @@ "cssnano": "^6.0.3", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "32.2.6", + "electron": "32.2.7", "eslint": "^9.11.1", "eslint-formatter-compact": "^8.40.0", "eslint-plugin-header": "3.1.1", From fca210cd103a496f25c23786b861a67f4d1ee16b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:56:24 +0100 Subject: [PATCH 468/479] Git - escape shell-sensitive characters (#236849) --- extensions/git/src/git.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 62bae1422df..5bdfd655dbc 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -348,10 +348,15 @@ function getGitErrorCode(stderr: string): string | undefined { return undefined; } -// https://github.com/microsoft/vscode/issues/89373 -// https://github.com/git-for-windows/git/issues/2478 function sanitizePath(path: string): string { - return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`); + return path + // Drive letter + // https://github.com/microsoft/vscode/issues/89373 + // https://github.com/git-for-windows/git/issues/2478 + .replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`) + // Shell-sensitive characters + // https://github.com/microsoft/vscode/issues/133566 + .replace(/(["'\\\$!><#()\[\]*&^| ;{}?`])/g, '\\$1'); } const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B'; From 4fa5611d67dc84e105e9cd155a746f2d7813d9a0 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 24 Dec 2024 02:02:40 +0100 Subject: [PATCH 469/479] Git - handle the diff editor for untracked files now that we throw `FileNotFound` if the file does not exist (#236863) --- extensions/git/src/fileSystemProvider.ts | 3 ++- extensions/git/src/repository.ts | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 24ae4e6df9a..0847fe8d745 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -192,7 +192,8 @@ export class GitFileSystemProvider implements FileSystemProvider { try { return await repository.buffer(sanitizeRef(ref, path, repository), path); } catch (err) { - // File does not exist in git (ex: git ignored) + // File does not exist in git. This could be + // because the file is untracked or ignored throw FileSystemError.FileNotFound(); } } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ec66d510c72..4bf7fa32ef0 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -560,13 +560,11 @@ class ResourceCommandResolver { switch (resource.type) { case Status.INDEX_MODIFIED: case Status.INDEX_RENAMED: - case Status.INDEX_ADDED: case Status.INTENT_TO_RENAME: case Status.TYPE_CHANGED: return { original: toGitUri(resource.original, 'HEAD') }; case Status.MODIFIED: - case Status.UNTRACKED: return { original: toGitUri(resource.resourceUri, '~') }; case Status.DELETED_BY_US: From 7da68c033c76bd21d5faabe830f255550a5c7710 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 27 Dec 2024 16:50:29 +1100 Subject: [PATCH 470/479] Remove console.log (#236006) --- .../browser/contrib/chatEdit/notebookSynchronizerService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts index 1e4cf151fae..9788828cf87 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts @@ -28,7 +28,6 @@ class NotebookSynchronizerSaveParticipant extends NotebookSaveParticipant { } override async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { - console.log('notebook synchronizer participate'); const session = this._chatEditingService.currentEditingSessionObs.get(); if (!session) { From 0a66dc39ff0a3476dd5f711df232cba4d713a57d Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Fri, 27 Dec 2024 14:31:53 -0500 Subject: [PATCH 471/479] Adopt concept of flows in Microsoft Auth (#237006) And only use Loopback flow when not running in Remote Extension Host. --- .../src/node/authProvider.ts | 125 ++++++++---------- .../src/node/flows.ts | 105 +++++++++++++++ 2 files changed, 159 insertions(+), 71 deletions(-) create mode 100644 extensions/microsoft-authentication/src/node/flows.ts diff --git a/extensions/microsoft-authentication/src/node/authProvider.ts b/extensions/microsoft-authentication/src/node/authProvider.ts index cc8eb2bc5c7..0a352c8eb86 100644 --- a/extensions/microsoft-authentication/src/node/authProvider.ts +++ b/extensions/microsoft-authentication/src/node/authProvider.ts @@ -3,18 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { AccountInfo, AuthenticationResult, ClientAuthError, ClientAuthErrorCodes, ServerError } from '@azure/msal-node'; -import { AuthenticationGetSessionOptions, AuthenticationProvider, AuthenticationProviderAuthenticationSessionsChangeEvent, AuthenticationProviderSessionOptions, AuthenticationSession, AuthenticationSessionAccountInformation, CancellationError, env, EventEmitter, ExtensionContext, l10n, LogOutputChannel, Uri, window } from 'vscode'; +import { AuthenticationGetSessionOptions, AuthenticationProvider, AuthenticationProviderAuthenticationSessionsChangeEvent, AuthenticationProviderSessionOptions, AuthenticationSession, AuthenticationSessionAccountInformation, CancellationError, EventEmitter, ExtensionContext, ExtensionKind, l10n, LogOutputChannel, window } from 'vscode'; import { Environment } from '@azure/ms-rest-azure-env'; import { CachedPublicClientApplicationManager } from './publicClientCache'; -import { UriHandlerLoopbackClient } from '../common/loopbackClientAndOpener'; import { UriEventHandler } from '../UriEventHandler'; import { ICachedPublicClientApplication } from '../common/publicClientCache'; import { MicrosoftAccountType, MicrosoftAuthenticationTelemetryReporter } from '../common/telemetryReporter'; -import { loopbackTemplate } from './loopbackTemplate'; import { ScopeData } from '../common/scopeData'; import { EventBufferer } from '../common/event'; import { BetterTokenStorage } from '../betterSecretStorage'; import { IStoredSession } from '../AADHelper'; +import { ExtensionHost, getMsalFlows } from './flows'; const redirectUri = 'https://vscode.dev/redirect'; const MSA_TID = '9188040d-6c67-4c5b-b112-36a304b66dad'; @@ -187,86 +186,70 @@ export class MsalAuthProvider implements AuthenticationProvider { this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'starting'); const cachedPca = await this.getOrCreatePublicClientApplication(scopeData.clientId, scopeData.tenant); - let result: AuthenticationResult | undefined; - - try { - const windowHandle = window.nativeHandle ? Buffer.from(window.nativeHandle) : undefined; - result = await cachedPca.acquireTokenInteractive({ - openBrowser: async (url: string) => { await env.openExternal(Uri.parse(url)); }, - scopes: scopeData.scopesToSend, - // The logic for rendering one or the other of these templates is in the - // template itself, so we pass the same one for both. - successTemplate: loopbackTemplate, - errorTemplate: loopbackTemplate, - // Pass the label of the account to the login hint so that we prefer signing in to that account - loginHint: options.account?.label, - // If we aren't logging in to a specific account, then we can use the prompt to make sure they get - // the option to choose a different account. - prompt: options.account?.label ? undefined : 'select_account', - windowHandle - }); - } catch (e) { - if (e instanceof CancellationError) { - const yes = l10n.t('Yes'); - const result = await window.showErrorMessage( - l10n.t('Having trouble logging in?'), - { - modal: true, - detail: l10n.t('Would you like to try a different way to sign in to your Microsoft account? ({0})', 'protocol handler') - }, - yes - ); - if (!result) { - this._telemetryReporter.sendLoginFailedEvent(); - throw e; - } + + // Used for showing a friendlier message to the user when the explicitly cancel a flow. + let userCancelled: boolean | undefined; + const yes = l10n.t('Yes'); + const no = l10n.t('No'); + const promptToContinue = async (mode: string) => { + if (userCancelled === undefined) { + // We haven't had a failure yet so wait to prompt + return; } - // This error comes from the backend and is likely not due to the user's machine - // failing to open a port or something local that would require us to try the - // URL handler loopback client. - if (e instanceof ServerError) { - this._telemetryReporter.sendLoginFailedEvent(); - throw e; + const message = userCancelled + ? l10n.t('Having trouble logging in? Would you like to try a different way? ({0})', mode) + : l10n.t('You have not yet finished authorizing this extension to use your Microsoft Account. Would you like to try a different way? ({0})', mode); + const result = await window.showWarningMessage(message, yes, no); + if (result !== yes) { + throw new CancellationError(); } + }; - // The user closed the modal window - if ((e as ClientAuthError).errorCode === ClientAuthErrorCodes.userCanceled) { - this._telemetryReporter.sendLoginFailedEvent(); - throw e; - } + const flows = getMsalFlows({ + extensionHost: typeof navigator === 'undefined' + ? this._context.extension.extensionKind === ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote + : ExtensionHost.WebWorker, + }); - // The user wants to try the loopback client or we got an error likely due to spinning up the server - const loopbackClient = new UriHandlerLoopbackClient(this._uriHandler, redirectUri, this._logger); + let lastError: Error | undefined; + for (const flow of flows) { + if (flow !== flows[0]) { + try { + await promptToContinue(flow.label); + } finally { + this._telemetryReporter.sendLoginFailedEvent(); + } + } try { - const windowHandle = window.nativeHandle ? Buffer.from(window.nativeHandle) : undefined; - result = await cachedPca.acquireTokenInteractive({ - openBrowser: (url: string) => loopbackClient.openBrowser(url), + const result = await flow.trigger({ + cachedPca, scopes: scopeData.scopesToSend, - loopbackClient, loginHint: options.account?.label, - prompt: options.account?.label ? undefined : 'select_account', - windowHandle + windowHandle: window.nativeHandle ? Buffer.from(window.nativeHandle) : undefined, + logger: this._logger, + uriHandler: this._uriHandler }); + + const session = this.sessionFromAuthenticationResult(result, scopeData.originalScopes); + this._telemetryReporter.sendLoginEvent(session.scopes); + this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'returned session'); + this._onDidChangeSessionsEmitter.fire({ added: [session], changed: [], removed: [] }); + return session; } catch (e) { - this._telemetryReporter.sendLoginFailedEvent(); - throw e; + lastError = e; + if (e instanceof ServerError || (e as ClientAuthError)?.errorCode === ClientAuthErrorCodes.userCanceled) { + this._telemetryReporter.sendLoginFailedEvent(); + throw e; + } + // Continue to next flow + if (e instanceof CancellationError) { + userCancelled = true; + } } } - if (!result) { - this._telemetryReporter.sendLoginFailedEvent(); - throw new Error('No result returned from MSAL'); - } - - const session = this.sessionFromAuthenticationResult(result, scopeData.originalScopes); - this._telemetryReporter.sendLoginEvent(session.scopes); - this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'returned session'); - // This is the only scenario in which we need to fire the _onDidChangeSessionsEmitter out of band... - // the badge flow (when the client passes no options in to getSession) will only remove a badge if a session - // was created that _matches the scopes_ that that badge requests. See `onDidChangeSessions` for more info. - // TODO: This should really be fixed in Core. - this._onDidChangeSessionsEmitter.fire({ added: [session], changed: [], removed: [] }); - return session; + this._telemetryReporter.sendLoginFailedEvent(); + throw lastError ?? new Error('No auth flow succeeded'); } async removeSession(sessionId: string): Promise { diff --git a/extensions/microsoft-authentication/src/node/flows.ts b/extensions/microsoft-authentication/src/node/flows.ts new file mode 100644 index 00000000000..3e1d8c513f0 --- /dev/null +++ b/extensions/microsoft-authentication/src/node/flows.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AuthenticationResult } from '@azure/msal-node'; +import { Uri, LogOutputChannel, env } from 'vscode'; +import { ICachedPublicClientApplication } from '../common/publicClientCache'; +import { UriHandlerLoopbackClient } from '../common/loopbackClientAndOpener'; +import { UriEventHandler } from '../UriEventHandler'; +import { loopbackTemplate } from './loopbackTemplate'; + +const redirectUri = 'https://vscode.dev/redirect'; + +export const enum ExtensionHost { + WebWorker, + Remote, + Local +} + +interface IMsalFlowOptions { + supportsRemoteExtensionHost: boolean; + supportsWebWorkerExtensionHost: boolean; +} + +interface IMsalFlowTriggerOptions { + cachedPca: ICachedPublicClientApplication; + scopes: string[]; + loginHint?: string; + windowHandle?: Buffer; + logger: LogOutputChannel; + uriHandler: UriEventHandler; +} + +interface IMsalFlow { + readonly label: string; + readonly options: IMsalFlowOptions; + trigger(options: IMsalFlowTriggerOptions): Promise; +} + +class DefaultLoopbackFlow implements IMsalFlow { + label = 'default'; + options: IMsalFlowOptions = { + supportsRemoteExtensionHost: true, + supportsWebWorkerExtensionHost: true + }; + + async trigger({ cachedPca, scopes, loginHint, windowHandle, logger }: IMsalFlowTriggerOptions): Promise { + logger.info('Trying default msal flow...'); + return await cachedPca.acquireTokenInteractive({ + openBrowser: async (url: string) => { await env.openExternal(Uri.parse(url)); }, + scopes, + successTemplate: loopbackTemplate, + errorTemplate: loopbackTemplate, + loginHint, + prompt: loginHint ? undefined : 'select_account', + windowHandle + }); + } +} + +class UrlHandlerFlow implements IMsalFlow { + label = 'protocol handler'; + options: IMsalFlowOptions = { + supportsRemoteExtensionHost: false, + supportsWebWorkerExtensionHost: false + }; + + async trigger({ cachedPca, scopes, loginHint, windowHandle, logger, uriHandler }: IMsalFlowTriggerOptions): Promise { + logger.info('Trying protocol handler flow...'); + const loopbackClient = new UriHandlerLoopbackClient(uriHandler, redirectUri, logger); + return await cachedPca.acquireTokenInteractive({ + openBrowser: (url: string) => loopbackClient.openBrowser(url), + scopes, + loopbackClient, + loginHint, + prompt: loginHint ? undefined : 'select_account', + windowHandle + }); + } +} + +const allFlows: IMsalFlow[] = [ + new DefaultLoopbackFlow(), + new UrlHandlerFlow() +]; + +export interface IMsalFlowQuery { + extensionHost: ExtensionHost; +} + +export function getMsalFlows(query: IMsalFlowQuery): IMsalFlow[] { + return allFlows.filter(flow => { + let useFlow: boolean = true; + switch (query.extensionHost) { + case ExtensionHost.Remote: + useFlow &&= flow.options.supportsRemoteExtensionHost; + break; + case ExtensionHost.WebWorker: + useFlow &&= flow.options.supportsWebWorkerExtensionHost; + break; + } + return useFlow; + }); +} From 1410d77f6fdd80ceb2e522449096fc97e821aa01 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Sat, 28 Dec 2024 03:36:05 -0500 Subject: [PATCH 472/479] reverse cancellation and sequencer (#237029) When we cancel, then the promise should be cancelled. If we don't do this, we hang on the first interaction request until we timeout. Fixes the 2nd point in https://github.com/microsoft/vscode/issues/236825#issuecomment-2563882150 --- .../src/node/cachedPublicClientApplication.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts index 0f27c2c0e4d..616b3e1120d 100644 --- a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts +++ b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts @@ -133,11 +133,11 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica cancellable: true, title: l10n.t('Signing in to Microsoft...') }, - (_process, token) => raceCancellationAndTimeoutError( - this._sequencer.queue(() => this._pca.acquireTokenInteractive(request)), + (_process, token) => this._sequencer.queue(() => raceCancellationAndTimeoutError( + this._pca.acquireTokenInteractive(request), token, 1000 * 60 * 5 - ) + )) ); // this._setupRefresh(result); if (this._isBrokerAvailable) { From a3261eae42b18ff8cb685cf649c27c8b27985eec Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Sun, 29 Dec 2024 08:44:19 +0100 Subject: [PATCH 473/479] Fix default tree find modes (#237057) fix #236770 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 27 ++++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 09ce418bda9..cff0e6614d3 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -947,7 +947,7 @@ interface IAbstractFindControllerOptions extends IFindWidgetOptions { showNotFoundMessage?: boolean; } -interface IFindControllerOptions extends IAbstractFindControllerOptions { +export interface IFindControllerOptions extends IAbstractFindControllerOptions { defaultFindMode?: TreeFindMode; defaultFindMatchType?: TreeFindMatchType; } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index dd6059051f0..f6c47bb9283 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -7,7 +7,7 @@ import { IDragAndDropData } from '../../dnd.js'; import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, IListVirtualDelegate } from '../list/list.js'; import { ElementsDragAndDropData, ListViewTargetSector } from '../list/listView.js'; import { IListStyles } from '../list/listWidget.js'; -import { ComposedTreeDelegate, TreeFindMode as TreeFindMode, IAbstractTreeOptions, IAbstractTreeOptionsUpdate, TreeFindMatchType, AbstractTreePart, LabelFuzzyScore, FindFilter, FindController, ITreeFindToggleChangeEvent } from './abstractTree.js'; +import { ComposedTreeDelegate, TreeFindMode as TreeFindMode, IAbstractTreeOptions, IAbstractTreeOptionsUpdate, TreeFindMatchType, AbstractTreePart, LabelFuzzyScore, FindFilter, FindController, ITreeFindToggleChangeEvent, IFindControllerOptions } from './abstractTree.js'; import { ICompressedTreeElement, ICompressedTreeNode } from './compressedObjectTreeModel.js'; import { getVisibleState, isFilterResult } from './indexTreeModel.js'; import { CompressibleObjectTree, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions, ICompressibleTreeRenderer, IObjectTreeOptions, IObjectTreeSetChildrenOptions, ObjectTree } from './objectTree.js'; @@ -629,7 +629,12 @@ export class AsyncDataTree implements IDisposable this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables); if (asyncFindEnabled) { - const findOptions = { styles: options.findWidgetStyles, showNotFoundMessage: options.showNotFoundMessage }; + const findOptions: IFindControllerOptions = { + styles: options.findWidgetStyles, + showNotFoundMessage: options.showNotFoundMessage, + defaultFindMatchType: options.defaultFindMatchType, + defaultFindMode: options.defaultFindMode, + }; this.findController = this.disposables.add(new AsyncFindController(this.tree, options.findProvider!, findFilter!, this.tree.options.contextViewProvider!, findOptions)); this.focusNavigationFilter = node => this.findController!.shouldFocusWhenNavigating(node); @@ -657,8 +662,18 @@ export class AsyncDataTree implements IDisposable return new ObjectTree(user, container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); } - updateOptions(options: IAsyncDataTreeOptionsUpdate = {}): void { - this.tree.updateOptions(options); + updateOptions(optionsUpdate: IAsyncDataTreeOptionsUpdate = {}): void { + if (this.findController) { + if (optionsUpdate.defaultFindMode !== undefined) { + this.findController.mode = optionsUpdate.defaultFindMode; + } + + if (optionsUpdate.defaultFindMatchType !== undefined) { + this.findController.matchType = optionsUpdate.defaultFindMatchType; + } + } + + this.tree.updateOptions(optionsUpdate); } get options(): IAsyncDataTreeOptions { @@ -1513,10 +1528,6 @@ export class CompressibleAsyncDataTree extends As }; } - override updateOptions(options: ICompressibleAsyncDataTreeOptionsUpdate = {}): void { - this.tree.updateOptions(options); - } - override getViewState(): IAsyncDataTreeViewState { if (!this.identityProvider) { throw new TreeError(this.user, 'Can\'t get tree view state without an identity provider'); From 6d6cfdc3a6a1836a29a7034d88958c0b91df5def Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 30 Dec 2024 21:10:42 +0100 Subject: [PATCH 474/479] Git - add git blame editor decoration hover provider (#237102) * Initial implementation * Refactor editor decoration type --- extensions/git/src/blame.ts | 190 +++++++++++++++++++++++++++--------- 1 file changed, 145 insertions(+), 45 deletions(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index 5c65fbcf1fc..753af17766a 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DecorationOptions, l10n, Position, Range, TextEditor, TextEditorChange, TextEditorDecorationType, TextEditorChangeKind, ThemeColor, Uri, window, workspace, EventEmitter, ConfigurationChangeEvent, StatusBarItem, StatusBarAlignment, Command, MarkdownString } from 'vscode'; +import { DecorationOptions, l10n, Position, Range, TextEditor, TextEditorChange, TextEditorDecorationType, TextEditorChangeKind, ThemeColor, Uri, window, workspace, EventEmitter, ConfigurationChangeEvent, StatusBarItem, StatusBarAlignment, Command, MarkdownString, languages, HoverProvider, CancellationToken, Hover, TextDocument } from 'vscode'; import { Model } from './model'; import { dispose, fromNow, IDisposable } from './util'; import { Repository } from './repository'; import { throttle } from './decorators'; -import { BlameInformation } from './git'; +import { BlameInformation, Commit } from './git'; import { fromGitUri, isGitUri } from './uri'; import { emojify, ensureEmojis } from './emoji'; import { getWorkingTreeAndIndexDiffInformation, getWorkingTreeDiffInformation } from './staging'; @@ -55,6 +55,15 @@ function mapModifiedLineNumberToOriginalLineNumber(lineNumber: number, changes: return lineNumber; } +function getEditorDecorationRange(lineNumber: number): Range { + const position = new Position(lineNumber, Number.MAX_SAFE_INTEGER); + return new Range(position, position); +} + +function isBlameInformation(object: any): object is BlameInformation { + return Array.isArray((object as BlameInformation).ranges); +} + type BlameInformationTemplateTokens = { readonly hash: string; readonly hashShort: string; @@ -191,32 +200,63 @@ export class GitBlameController { }); } - getBlameInformationHover(documentUri: Uri, blameInformation: BlameInformation | string): MarkdownString { - if (typeof blameInformation === 'string') { - return new MarkdownString(blameInformation, true); + async getBlameInformationDetailedHover(documentUri: Uri, blameInformation: BlameInformation): Promise { + const repository = this._model.getRepository(documentUri); + if (!repository) { + return this.getBlameInformationHover(documentUri, blameInformation); + } + + try { + const commit = await repository.getCommit(blameInformation.hash); + return this.getBlameInformationHover(documentUri, commit); + } catch { + return this.getBlameInformationHover(documentUri, blameInformation); } + } + getBlameInformationHover(documentUri: Uri, blameInformationOrCommit: BlameInformation | Commit): MarkdownString { const markdownString = new MarkdownString(); - markdownString.supportThemeIcons = true; markdownString.isTrusted = true; + markdownString.supportHtml = true; + markdownString.supportThemeIcons = true; - if (blameInformation.authorName) { - markdownString.appendMarkdown(`$(account) **${blameInformation.authorName}**`); + if (blameInformationOrCommit.authorName) { + markdownString.appendMarkdown(`$(account) **${blameInformationOrCommit.authorName}**`); - if (blameInformation.authorDate) { - const dateString = new Date(blameInformation.authorDate).toLocaleString(undefined, { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }); - markdownString.appendMarkdown(`, $(history) ${fromNow(blameInformation.authorDate, true, true)} (${dateString})`); + if (blameInformationOrCommit.authorDate) { + const dateString = new Date(blameInformationOrCommit.authorDate).toLocaleString(undefined, { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }); + markdownString.appendMarkdown(`, $(history) ${fromNow(blameInformationOrCommit.authorDate, true, true)} (${dateString})`); } markdownString.appendMarkdown('\n\n'); } - markdownString.appendMarkdown(`${emojify(blameInformation.subject ?? '')}\n\n`); + markdownString.appendMarkdown(`${emojify(isBlameInformation(blameInformationOrCommit) ? blameInformationOrCommit.subject ?? '' : blameInformationOrCommit.message)}\n\n`); markdownString.appendMarkdown(`---\n\n`); - markdownString.appendMarkdown(`[$(eye) View Commit](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformation.hash]))} "${l10n.t('View Commit')}")`); + if (!isBlameInformation(blameInformationOrCommit) && blameInformationOrCommit.shortStat) { + markdownString.appendMarkdown(`${blameInformationOrCommit.shortStat.files === 1 ? + l10n.t('{0} file changed', blameInformationOrCommit.shortStat.files) : + l10n.t('{0} files changed', blameInformationOrCommit.shortStat.files)}`); + + if (blameInformationOrCommit.shortStat.insertions) { + markdownString.appendMarkdown(`, ${blameInformationOrCommit.shortStat.insertions === 1 ? + l10n.t('{0} insertion{1}', blameInformationOrCommit.shortStat.insertions, '(+)') : + l10n.t('{0} insertions{1}', blameInformationOrCommit.shortStat.insertions, '(+)')}`); + } + + if (blameInformationOrCommit.shortStat.deletions) { + markdownString.appendMarkdown(`, ${blameInformationOrCommit.shortStat.deletions === 1 ? + l10n.t('{0} deletion{1}', blameInformationOrCommit.shortStat.deletions, '(-)') : + l10n.t('{0} deletions{1}', blameInformationOrCommit.shortStat.deletions, '(-)')}`); + } + + markdownString.appendMarkdown(`\n\n---\n\n`); + } + + markdownString.appendMarkdown(`[$(eye) View Commit](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformationOrCommit.hash]))} "${l10n.t('View Commit')}")`); markdownString.appendMarkdown('    '); - markdownString.appendMarkdown(`[$(copy) ${blameInformation.hash.substring(0, 8)}](command:git.blameStatusBarItem.copyContent?${encodeURIComponent(JSON.stringify(blameInformation.hash))} "${l10n.t('Copy Commit Hash')}")`); + markdownString.appendMarkdown(`[$(copy) ${blameInformationOrCommit.hash.substring(0, 8)}](command:git.blameStatusBarItem.copyContent?${encodeURIComponent(JSON.stringify(blameInformationOrCommit.hash))} "${l10n.t('Copy Commit Hash')}")`); return markdownString; } @@ -411,36 +451,81 @@ export class GitBlameController { } } -class GitBlameEditorDecoration { - private readonly _decorationType: TextEditorDecorationType; +class GitBlameEditorDecoration implements HoverProvider { + private _decoration: TextEditorDecorationType | undefined; + private get decoration(): TextEditorDecorationType { + if (!this._decoration) { + this._decoration = window.createTextEditorDecorationType({ + after: { + color: new ThemeColor('git.blame.editorDecorationForeground') + } + }); + } + + return this._decoration; + } + + private _hoverDisposable: IDisposable | undefined; private _disposables: IDisposable[] = []; constructor(private readonly _controller: GitBlameController) { - this._decorationType = window.createTextEditorDecorationType({ - after: { - color: new ThemeColor('git.blame.editorDecorationForeground') - } - }); - this._disposables.push(this._decorationType); - workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); window.onDidChangeActiveTextEditor(this._onDidChangeActiveTextEditor, this, this._disposables); - this._controller.onDidChangeBlameInformation(e => this._updateDecorations(e), this, this._disposables); + + this._onDidChangeConfiguration(); } - private _onDidChangeConfiguration(e: ConfigurationChangeEvent): void { - if (!e.affectsConfiguration('git.blame.editorDecoration.enabled') && + async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return undefined; + } + + const textEditor = window.activeTextEditor; + if (!textEditor) { + return undefined; + } + + // Position must be at the end of the line + if (position.character !== document.lineAt(position.line).range.end.character) { + return undefined; + } + + // Get blame information + const blameInformation = this._controller.textEditorBlameInformation + .get(textEditor)?.find(blame => blame.lineNumber === position.line); + + if (!blameInformation || typeof blameInformation.blameInformation === 'string') { + return undefined; + } + + const contents = await this._controller.getBlameInformationDetailedHover(textEditor.document.uri, blameInformation.blameInformation); + + if (!contents || token.isCancellationRequested) { + return undefined; + } + + return { range: getEditorDecorationRange(position.line), contents: [contents] }; + } + + private _onDidChangeConfiguration(e?: ConfigurationChangeEvent): void { + if (e && + !e.affectsConfiguration('git.blame.editorDecoration.enabled') && !e.affectsConfiguration('git.blame.editorDecoration.template')) { return; } - for (const textEditor of window.visibleTextEditors) { - if (this._getConfiguration().enabled) { - this._updateDecorations(textEditor); - } else { - textEditor.setDecorations(this._decorationType, []); + if (this._getConfiguration().enabled) { + if (window.activeTextEditor) { + this._registerHoverProvider(); + this._updateDecorations(window.activeTextEditor); } + } else { + this._decoration?.dispose(); + this._decoration = undefined; + + this._hoverDisposable?.dispose(); + this._hoverDisposable = undefined; } } @@ -449,11 +534,15 @@ class GitBlameEditorDecoration { return; } + // Clear decorations for (const editor of window.visibleTextEditors) { if (editor !== window.activeTextEditor) { - editor.setDecorations(this._decorationType, []); + editor.setDecorations(this.decoration, []); } } + + // Register hover provider + this._registerHoverProvider(); } private _getConfiguration(): { enabled: boolean; template: string } { @@ -472,14 +561,14 @@ class GitBlameEditorDecoration { // Only support resources with `file` and `git` schemes if (textEditor.document.uri.scheme !== 'file' && !isGitUri(textEditor.document.uri)) { - textEditor.setDecorations(this._decorationType, []); + textEditor.setDecorations(this.decoration, []); return; } // Get blame information const blameInformation = this._controller.textEditorBlameInformation.get(textEditor); if (!blameInformation) { - textEditor.setDecorations(this._decorationType, []); + textEditor.setDecorations(this.decoration, []); return; } @@ -488,32 +577,43 @@ class GitBlameEditorDecoration { const contentText = typeof blame.blameInformation !== 'string' ? this._controller.formatBlameInformationMessage(template, blame.blameInformation) : blame.blameInformation; - const hoverMessage = typeof blame.blameInformation !== 'string' - ? this._controller.getBlameInformationHover(textEditor.document.uri, blame.blameInformation) - : undefined; - return this._createDecoration(blame.lineNumber, contentText, hoverMessage); + return this._createDecoration(blame.lineNumber, contentText); }); - textEditor.setDecorations(this._decorationType, decorations); + textEditor.setDecorations(this.decoration, decorations); } - private _createDecoration(lineNumber: number, contentText: string, hoverMessage: MarkdownString | undefined): DecorationOptions { - const position = new Position(lineNumber, Number.MAX_SAFE_INTEGER); - + private _createDecoration(lineNumber: number, contentText: string): DecorationOptions { return { - hoverMessage, - range: new Range(position, position), + range: getEditorDecorationRange(lineNumber), renderOptions: { after: { - contentText: `${contentText}`, + contentText, margin: '0 0 0 50px' } }, }; } + private _registerHoverProvider(): void { + this._hoverDisposable?.dispose(); + + if (window.activeTextEditor?.document.uri.scheme === 'file' || + window.activeTextEditor?.document.uri.scheme === 'git') { + this._hoverDisposable = languages.registerHoverProvider({ + pattern: window.activeTextEditor.document.uri.fsPath + }, this); + } + } + dispose() { + this._decoration?.dispose(); + this._decoration = undefined; + + this._hoverDisposable?.dispose(); + this._hoverDisposable = undefined; + this._disposables = dispose(this._disposables); } } From ba56ac5291229995c0e0907127668461551911e7 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:44:45 +0100 Subject: [PATCH 475/479] Git - refactor git blame code (#237185) Refactor git blame code --- extensions/git/src/blame.ts | 259 ++++++++++++++++++------------------ 1 file changed, 127 insertions(+), 132 deletions(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index 753af17766a..3893b4bff4f 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -64,6 +64,10 @@ function isBlameInformation(object: any): object is BlameInformation { return Array.isArray((object as BlameInformation).ranges); } +function isResourceSchemeSupported(uri: Uri): boolean { + return uri.scheme === 'file' || isGitUri(uri); +} + type BlameInformationTemplateTokens = { readonly hash: string; readonly hashShort: string; @@ -156,28 +160,30 @@ class GitBlameInformationCache { export class GitBlameController { private readonly _subjectMaxLength = 50; - private readonly _onDidChangeBlameInformation = new EventEmitter(); + private readonly _onDidChangeBlameInformation = new EventEmitter(); public readonly onDidChangeBlameInformation = this._onDidChangeBlameInformation.event; - readonly textEditorBlameInformation = new Map(); + private _textEditorBlameInformation: LineBlameInformation[] | undefined; + get textEditorBlameInformation(): readonly LineBlameInformation[] | undefined { + return this._textEditorBlameInformation; + } + private set textEditorBlameInformation(blameInformation: LineBlameInformation[] | undefined) { + this._textEditorBlameInformation = blameInformation; + this._onDidChangeBlameInformation.fire(); + } private readonly _repositoryBlameCache = new GitBlameInformationCache(); + private _editorDecoration: GitBlameEditorDecoration | undefined; + private _statusBarItem: GitBlameStatusBarItem | undefined; + private _repositoryDisposables = new Map(); + private _enablementDisposables: IDisposable[] = []; private _disposables: IDisposable[] = []; constructor(private readonly _model: Model) { - this._disposables.push(new GitBlameEditorDecoration(this)); - this._disposables.push(new GitBlameStatusBarItem(this)); - - this._model.onDidOpenRepository(this._onDidOpenRepository, this, this._disposables); - this._model.onDidCloseRepository(this._onDidCloseRepository, this, this._disposables); - - window.onDidChangeActiveTextEditor(e => this._updateTextEditorBlameInformation(e), this, this._disposables); - window.onDidChangeTextEditorSelection(e => this._updateTextEditorBlameInformation(e.textEditor, true), this, this._disposables); - window.onDidChangeTextEditorDiffInformation(e => this._updateTextEditorBlameInformation(e.textEditor), this, this._disposables); - - this._updateTextEditorBlameInformation(window.activeTextEditor); + workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); + this._onDidChangeConfiguration(); } formatBlameInformationMessage(template: string, blameInformation: BlameInformation): string { @@ -261,6 +267,57 @@ export class GitBlameController { return markdownString; } + private _onDidChangeConfiguration(e?: ConfigurationChangeEvent): void { + if (e && + !e.affectsConfiguration('git.blame.editorDecoration.enabled') && + !e.affectsConfiguration('git.blame.statusBarItem.enabled')) { + return; + } + + const config = workspace.getConfiguration('git'); + const editorDecorationEnabled = config.get('blame.editorDecoration.enabled') === true; + const statusBarItemEnabled = config.get('blame.statusBarItem.enabled') === true; + + // Editor decoration + if (editorDecorationEnabled) { + if (!this._editorDecoration) { + this._editorDecoration = new GitBlameEditorDecoration(this); + } + } else { + this._editorDecoration?.dispose(); + this._editorDecoration = undefined; + } + + // StatusBar item + if (statusBarItemEnabled) { + if (!this._statusBarItem) { + this._statusBarItem = new GitBlameStatusBarItem(this); + } + } else { + this._statusBarItem?.dispose(); + this._statusBarItem = undefined; + } + + // Listeners + if (editorDecorationEnabled || statusBarItemEnabled) { + if (this._enablementDisposables.length === 0) { + this._model.onDidOpenRepository(this._onDidOpenRepository, this, this._enablementDisposables); + this._model.onDidCloseRepository(this._onDidCloseRepository, this, this._enablementDisposables); + for (const repository of this._model.repositories) { + this._onDidOpenRepository(repository); + } + + window.onDidChangeActiveTextEditor(e => this._updateTextEditorBlameInformation(e), this, this._enablementDisposables); + window.onDidChangeTextEditorSelection(e => this._updateTextEditorBlameInformation(e.textEditor, true), this, this._enablementDisposables); + window.onDidChangeTextEditorDiffInformation(e => this._updateTextEditorBlameInformation(e.textEditor), this, this._enablementDisposables); + } + } else { + this._enablementDisposables = dispose(this._enablementDisposables); + } + + this._updateTextEditorBlameInformation(window.activeTextEditor); + } + private _onDidOpenRepository(repository: Repository): void { const repositoryDisposables: IDisposable[] = []; repository.onDidRunGitStatus(() => this._onDidRunGitStatus(repository), this, repositoryDisposables); @@ -290,9 +347,7 @@ export class GitBlameController { this._repositoryBlameCache.deleteBlameInformation(repository, 'file'); this._repositoryBlameCache.setRepositoryHEAD(repository, repository.HEAD.commit); - for (const textEditor of window.visibleTextEditors) { - this._updateTextEditorBlameInformation(textEditor); - } + this._updateTextEditorBlameInformation(window.activeTextEditor); } } @@ -320,7 +375,12 @@ export class GitBlameController { @throttle private async _updateTextEditorBlameInformation(textEditor: TextEditor | undefined, showBlameInformationForPositionZero = false): Promise { - if (!textEditor?.diffInformation || textEditor !== window.activeTextEditor) { + if (textEditor) { + if (!textEditor.diffInformation || textEditor !== window.activeTextEditor) { + return; + } + } else { + this.textEditorBlameInformation = undefined; return; } @@ -329,14 +389,19 @@ export class GitBlameController { return; } + // Only support resources with `file` and `git` schemes + if (!isResourceSchemeSupported(textEditor.document.uri)) { + this.textEditorBlameInformation = undefined; + return; + } + // Do not show blame information when there is a single selection and it is at the beginning // of the file [0, 0, 0, 0] unless the user explicitly navigates the cursor there. We do this // to avoid showing blame information when the editor is not focused. if (!showBlameInformationForPositionZero && textEditor.selections.length === 1 && textEditor.selections[0].start.line === 0 && textEditor.selections[0].start.character === 0 && textEditor.selections[0].end.line === 0 && textEditor.selections[0].end.character === 0) { - this.textEditorBlameInformation.set(textEditor, []); - this._onDidChangeBlameInformation.fire(textEditor); + this.textEditorBlameInformation = undefined; return; } @@ -437,8 +502,7 @@ export class GitBlameController { } } - this.textEditorBlameInformation.set(textEditor, lineBlameInformation); - this._onDidChangeBlameInformation.fire(textEditor); + this.textEditorBlameInformation = lineBlameInformation; } dispose() { @@ -452,26 +516,22 @@ export class GitBlameController { } class GitBlameEditorDecoration implements HoverProvider { - private _decoration: TextEditorDecorationType | undefined; - private get decoration(): TextEditorDecorationType { - if (!this._decoration) { - this._decoration = window.createTextEditorDecorationType({ - after: { - color: new ThemeColor('git.blame.editorDecorationForeground') - } - }); - } - - return this._decoration; - } + private _decoration: TextEditorDecorationType; private _hoverDisposable: IDisposable | undefined; private _disposables: IDisposable[] = []; constructor(private readonly _controller: GitBlameController) { + this._decoration = window.createTextEditorDecorationType({ + after: { + color: new ThemeColor('git.blame.editorDecorationForeground') + } + }); + this._disposables.push(this._decoration); + workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); window.onDidChangeActiveTextEditor(this._onDidChangeActiveTextEditor, this, this._disposables); - this._controller.onDidChangeBlameInformation(e => this._updateDecorations(e), this, this._disposables); + this._controller.onDidChangeBlameInformation(() => this._onDidChangeBlameInformation(), this, this._disposables); this._onDidChangeConfiguration(); } @@ -492,14 +552,14 @@ class GitBlameEditorDecoration implements HoverProvider { } // Get blame information - const blameInformation = this._controller.textEditorBlameInformation - .get(textEditor)?.find(blame => blame.lineNumber === position.line); + const blameInformation = this._controller.textEditorBlameInformation; + const lineBlameInformation = blameInformation?.find(blame => blame.lineNumber === position.line); - if (!blameInformation || typeof blameInformation.blameInformation === 'string') { + if (!lineBlameInformation || typeof lineBlameInformation.blameInformation === 'string') { return undefined; } - const contents = await this._controller.getBlameInformationDetailedHover(textEditor.document.uri, blameInformation.blameInformation); + const contents = await this._controller.getBlameInformationDetailedHover(textEditor.document.uri, lineBlameInformation.blameInformation); if (!contents || token.isCancellationRequested) { return undefined; @@ -509,35 +569,19 @@ class GitBlameEditorDecoration implements HoverProvider { } private _onDidChangeConfiguration(e?: ConfigurationChangeEvent): void { - if (e && - !e.affectsConfiguration('git.blame.editorDecoration.enabled') && - !e.affectsConfiguration('git.blame.editorDecoration.template')) { + if (e && !e.affectsConfiguration('git.blame.editorDecoration.template')) { return; } - if (this._getConfiguration().enabled) { - if (window.activeTextEditor) { - this._registerHoverProvider(); - this._updateDecorations(window.activeTextEditor); - } - } else { - this._decoration?.dispose(); - this._decoration = undefined; - - this._hoverDisposable?.dispose(); - this._hoverDisposable = undefined; - } + this._registerHoverProvider(); + this._onDidChangeBlameInformation(); } private _onDidChangeActiveTextEditor(): void { - if (!this._getConfiguration().enabled) { - return; - } - // Clear decorations for (const editor of window.visibleTextEditors) { if (editor !== window.activeTextEditor) { - editor.setDecorations(this.decoration, []); + editor.setDecorations(this._decoration, []); } } @@ -545,34 +589,23 @@ class GitBlameEditorDecoration implements HoverProvider { this._registerHoverProvider(); } - private _getConfiguration(): { enabled: boolean; template: string } { - const config = workspace.getConfiguration('git'); - const enabled = config.get('blame.editorDecoration.enabled', false); - const template = config.get('blame.editorDecoration.template', '${subject}, ${authorName} (${authorDateAgo})'); - - return { enabled, template }; - } - - private _updateDecorations(textEditor: TextEditor): void { - const { enabled, template } = this._getConfiguration(); - if (!enabled) { - return; - } - - // Only support resources with `file` and `git` schemes - if (textEditor.document.uri.scheme !== 'file' && !isGitUri(textEditor.document.uri)) { - textEditor.setDecorations(this.decoration, []); + private _onDidChangeBlameInformation(): void { + const textEditor = window.activeTextEditor; + if (!textEditor) { return; } // Get blame information - const blameInformation = this._controller.textEditorBlameInformation.get(textEditor); + const blameInformation = this._controller.textEditorBlameInformation; if (!blameInformation) { - textEditor.setDecorations(this.decoration, []); + textEditor.setDecorations(this._decoration, []); return; } // Set decorations for the editor + const config = workspace.getConfiguration('git'); + const template = config.get('blame.editorDecoration.template', '${subject}, ${authorName} (${authorDateAgo})'); + const decorations = blameInformation.map(blame => { const contentText = typeof blame.blameInformation !== 'string' ? this._controller.formatBlameInformationMessage(template, blame.blameInformation) @@ -581,7 +614,7 @@ class GitBlameEditorDecoration implements HoverProvider { return this._createDecoration(blame.lineNumber, contentText); }); - textEditor.setDecorations(this.decoration, decorations); + textEditor.setDecorations(this._decoration, decorations); } private _createDecoration(lineNumber: number, contentText: string): DecorationOptions { @@ -599,8 +632,7 @@ class GitBlameEditorDecoration implements HoverProvider { private _registerHoverProvider(): void { this._hoverDisposable?.dispose(); - if (window.activeTextEditor?.document.uri.scheme === 'file' || - window.activeTextEditor?.document.uri.scheme === 'git') { + if (window.activeTextEditor && isResourceSchemeSupported(window.activeTextEditor.document.uri)) { this._hoverDisposable = languages.registerHoverProvider({ pattern: window.activeTextEditor.document.uri.fsPath }, this); @@ -608,9 +640,6 @@ class GitBlameEditorDecoration implements HoverProvider { } dispose() { - this._decoration?.dispose(); - this._decoration = undefined; - this._hoverDisposable?.dispose(); this._hoverDisposable = undefined; @@ -619,70 +648,33 @@ class GitBlameEditorDecoration implements HoverProvider { } class GitBlameStatusBarItem { - private _statusBarItem: StatusBarItem | undefined; - + private _statusBarItem: StatusBarItem; private _disposables: IDisposable[] = []; constructor(private readonly _controller: GitBlameController) { - workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); - window.onDidChangeActiveTextEditor(this._onDidChangeActiveTextEditor, this, this._disposables); + this._statusBarItem = window.createStatusBarItem('git.blame', StatusBarAlignment.Right, 200); + this._statusBarItem.name = l10n.t('Git Blame Information'); + this._disposables.push(this._statusBarItem); - this._controller.onDidChangeBlameInformation(e => this._updateStatusBarItem(e), this, this._disposables); + workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); + this._controller.onDidChangeBlameInformation(() => this._onDidChangeBlameInformation(), this, this._disposables); } private _onDidChangeConfiguration(e: ConfigurationChangeEvent): void { - if (!e.affectsConfiguration('git.blame.statusBarItem.enabled') && - !e.affectsConfiguration('git.blame.statusBarItem.template')) { + if (!e.affectsConfiguration('git.blame.statusBarItem.template')) { return; } - if (this._getConfiguration().enabled) { - if (window.activeTextEditor) { - this._updateStatusBarItem(window.activeTextEditor); - } - } else { - this._statusBarItem?.dispose(); - this._statusBarItem = undefined; - } + this._onDidChangeBlameInformation(); } - private _onDidChangeActiveTextEditor(): void { - if (!this._getConfiguration().enabled) { - return; - } - + private _onDidChangeBlameInformation(): void { if (!window.activeTextEditor) { - this._statusBarItem?.hide(); - } - } - - private _getConfiguration(): { enabled: boolean; template: string } { - const config = workspace.getConfiguration('git'); - const enabled = config.get('blame.statusBarItem.enabled', false); - const template = config.get('blame.statusBarItem.template', '${authorName} (${authorDateAgo})'); - - return { enabled, template }; - } - - private _updateStatusBarItem(textEditor: TextEditor): void { - const { enabled, template } = this._getConfiguration(); - if (!enabled || textEditor !== window.activeTextEditor) { - return; - } - - if (!this._statusBarItem) { - this._statusBarItem = window.createStatusBarItem('git.blame', StatusBarAlignment.Right, 200); - this._statusBarItem.name = l10n.t('Git Blame Information'); - this._disposables.push(this._statusBarItem); - } - - // Only support resources with `file` and `git` schemes - if (textEditor.document.uri.scheme !== 'file' && !isGitUri(textEditor.document.uri)) { this._statusBarItem.hide(); return; } - const blameInformation = this._controller.textEditorBlameInformation.get(textEditor); + const blameInformation = this._controller.textEditorBlameInformation; if (!blameInformation || blameInformation.length === 0) { this._statusBarItem.hide(); return; @@ -693,12 +685,15 @@ class GitBlameStatusBarItem { this._statusBarItem.tooltip = l10n.t('Git Blame Information'); this._statusBarItem.command = undefined; } else { + const config = workspace.getConfiguration('git'); + const template = config.get('blame.statusBarItem.template', '${authorName} (${authorDateAgo})'); + this._statusBarItem.text = `$(git-commit) ${this._controller.formatBlameInformationMessage(template, blameInformation[0].blameInformation)}`; - this._statusBarItem.tooltip = this._controller.getBlameInformationHover(textEditor.document.uri, blameInformation[0].blameInformation); + this._statusBarItem.tooltip = this._controller.getBlameInformationHover(window.activeTextEditor.document.uri, blameInformation[0].blameInformation); this._statusBarItem.command = { title: l10n.t('View Commit'), command: 'git.blameStatusBarItem.viewCommit', - arguments: [textEditor.document.uri, blameInformation[0].blameInformation.hash] + arguments: [window.activeTextEditor.document.uri, blameInformation[0].blameInformation.hash] } satisfies Command; } From 92bae09f4dba58bb5eed52d75ba22ffd4424e1c6 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:45:45 +0100 Subject: [PATCH 476/479] Git - update git blame settings (#237187) --- extensions/git/package.json | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 4556255d3da..748ac40f1ae 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3203,38 +3203,22 @@ "git.blame.editorDecoration.enabled": { "type": "boolean", "default": false, - "markdownDescription": "%config.blameEditorDecoration.enabled%", - "scope": "resource", - "tags": [ - "experimental" - ] + "markdownDescription": "%config.blameEditorDecoration.enabled%" }, "git.blame.editorDecoration.template": { "type": "string", "default": "${subject}, ${authorName} (${authorDateAgo})", - "markdownDescription": "%config.blameEditorDecoration.template%", - "scope": "resource", - "tags": [ - "experimental" - ] + "markdownDescription": "%config.blameEditorDecoration.template%" }, "git.blame.statusBarItem.enabled": { "type": "boolean", "default": false, - "markdownDescription": "%config.blameStatusBarItem.enabled%", - "scope": "resource", - "tags": [ - "experimental" - ] + "markdownDescription": "%config.blameStatusBarItem.enabled%" }, "git.blame.statusBarItem.template": { "type": "string", "default": "${authorName} (${authorDateAgo})", - "markdownDescription": "%config.blameStatusBarItem.template%", - "scope": "resource", - "tags": [ - "experimental" - ] + "markdownDescription": "%config.blameStatusBarItem.template%" } } }, From 2bdb3e9b41bd72048ea2067a350d8536c82fc7f6 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:46:09 +0100 Subject: [PATCH 477/479] Git - git blame hover commands polish (#237190) --- extensions/git/src/blame.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index 3893b4bff4f..3c7d61dd1e1 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -260,9 +260,11 @@ export class GitBlameController { markdownString.appendMarkdown(`\n\n---\n\n`); } - markdownString.appendMarkdown(`[$(eye) View Commit](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformationOrCommit.hash]))} "${l10n.t('View Commit')}")`); - markdownString.appendMarkdown('    '); - markdownString.appendMarkdown(`[$(copy) ${blameInformationOrCommit.hash.substring(0, 8)}](command:git.blameStatusBarItem.copyContent?${encodeURIComponent(JSON.stringify(blameInformationOrCommit.hash))} "${l10n.t('Copy Commit Hash')}")`); + markdownString.appendMarkdown(`[\`$(git-commit) ${blameInformationOrCommit.hash.substring(0, 8)} \`](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformationOrCommit.hash]))} "${l10n.t('View Commit')}")`); + markdownString.appendMarkdown(' '); + markdownString.appendMarkdown(`[$(copy)](command:git.blameStatusBarItem.copyContent?${encodeURIComponent(JSON.stringify(blameInformationOrCommit.hash))} "${l10n.t('Copy Commit Hash')}")`); + markdownString.appendMarkdown('  |  '); + markdownString.appendMarkdown(`[$(gear)](command:workbench.action.openSettings?%5B%22git.blame%22%5D "${l10n.t('Open Settings')}")`); return markdownString; } From c97bc9def4cabb7fd445607b00e97ddcd2b6afe7 Mon Sep 17 00:00:00 2001 From: Roman Nikitenko Date: Fri, 3 Jan 2025 16:39:23 +0200 Subject: [PATCH 478/479] chore: Update lock files after resolving conflicts Signed-off-by: Roman Nikitenko rh-pre-commit.version: 2.2.0 rh-pre-commit.check-secrets: ENABLED --- .../package-lock.json | 2 +- code/extensions/package-lock.json | 1 + code/package-lock.json | 22 ------ code/remote/package-lock.json | 78 +++++++++---------- 4 files changed, 41 insertions(+), 62 deletions(-) diff --git a/code/extensions/microsoft-authentication/package-lock.json b/code/extensions/microsoft-authentication/package-lock.json index 538bbc19d78..0ee986f3e6d 100644 --- a/code/extensions/microsoft-authentication/package-lock.json +++ b/code/extensions/microsoft-authentication/package-lock.json @@ -16,7 +16,7 @@ "@azure/msal-node": "^2.16.2", "@azure/msal-node-extensions": "^1.5.0", "@vscode/extension-telemetry": "^0.9.8", - "keytar": "workspace:*", + "keytar": "workspace:*", "vscode-tas-client": "^0.1.84" }, "devDependencies": { diff --git a/code/extensions/package-lock.json b/code/extensions/package-lock.json index 6ffc720a89b..aa142087652 100644 --- a/code/extensions/package-lock.json +++ b/code/extensions/package-lock.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@parcel/watcher": "2.5.0", + "crypto": "1.0.1", "esbuild": "0.23.0", "vscode-grammar-updater": "^1.1.0" } diff --git a/code/package-lock.json b/code/package-lock.json index 4cec82c0248..40ac8d8d8a2 100644 --- a/code/package-lock.json +++ b/code/package-lock.json @@ -12577,28 +12577,6 @@ } } }, - "node_modules/node-html-markdown": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-html-markdown/-/node-html-markdown-1.3.0.tgz", - "integrity": "sha512-OeFi3QwC/cPjvVKZ114tzzu+YoR+v9UXW5RwSXGUqGb0qCl0DvP406tzdL7SFn8pZrMyzXoisfG2zcuF9+zw4g==", - "dev": true, - "dependencies": { - "node-html-parser": "^6.1.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/node-html-parser": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", - "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", - "dev": true, - "dependencies": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, "node_modules/node-pty": { "version": "1.1.0-beta22", "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta22.tgz", diff --git a/code/remote/package-lock.json b/code/remote/package-lock.json index 16818856cbd..5d5d092f103 100644 --- a/code/remote/package-lock.json +++ b/code/remote/package-lock.json @@ -1151,6 +1151,33 @@ "node": ">=8" } }, + "node_modules/font-finder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", + "integrity": "sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==", + "license": "MIT", + "dependencies": { + "get-system-fonts": "^2.0.0", + "promise-stream-reader": "^1.0.1" + }, + "engines": { + "node": ">8.0.0" + } + }, + "node_modules/font-ligatures": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz", + "integrity": "sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==", + "license": "MIT", + "dependencies": { + "font-finder": "^1.0.3", + "lru-cache": "^6.0.0", + "opentype.js": "^0.8.0" + }, + "engines": { + "node": ">8.0.0" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -1190,33 +1217,6 @@ "node": ">= 0.12" } }, - "node_modules/font-finder": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", - "integrity": "sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==", - "license": "MIT", - "dependencies": { - "get-system-fonts": "^2.0.0", - "promise-stream-reader": "^1.0.1" - }, - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/font-ligatures": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz", - "integrity": "sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==", - "license": "MIT", - "dependencies": { - "font-finder": "^1.0.3", - "lru-cache": "^6.0.0", - "opentype.js": "^0.8.0" - }, - "engines": { - "node": ">8.0.0" - } - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -1788,6 +1788,18 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/opentype.js": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.8.0.tgz", + "integrity": "sha512-FQHR4oGP+a0m/f6yHoRpBOIbn/5ZWxKd4D/djHVJu8+KpBTYrJda0b7mLcgDEMWXE9xBCJm+qb0yv6FcvPjukg==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.2" + }, + "bin": { + "ot": "bin/ot" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -1825,18 +1837,6 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, - "node_modules/opentype.js": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.8.0.tgz", - "integrity": "sha512-FQHR4oGP+a0m/f6yHoRpBOIbn/5ZWxKd4D/djHVJu8+KpBTYrJda0b7mLcgDEMWXE9xBCJm+qb0yv6FcvPjukg==", - "license": "MIT", - "dependencies": { - "tiny-inflate": "^1.0.2" - }, - "bin": { - "ot": "bin/ot" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", From f14588e5cb7a9be4dbc7331f701f8f1460f47e0a Mon Sep 17 00:00:00 2001 From: Roman Nikitenko Date: Tue, 7 Jan 2025 19:37:02 +0000 Subject: [PATCH 479/479] fix: Fix fetching product.json file content Signed-off-by: Roman Nikitenko --- code/src/vs/platform/product/common/che/product.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/src/vs/platform/product/common/che/product.ts b/code/src/vs/platform/product/common/che/product.ts index 1c261e11f7c..d91f8f6f19b 100644 --- a/code/src/vs/platform/product/common/che/product.ts +++ b/code/src/vs/platform/product/common/che/product.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2024 Red Hat, Inc. + * Copyright (c) 2024-2025 Red Hat, Inc. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,17 +9,17 @@ ***********************************************************************/ /* eslint-disable header/header */ +import { FileAccess } from '../../../../base/common/network.js'; import { IProductConfiguration } from '../../../../base/common/product.js'; export function loadFromFileSystem(): IProductConfiguration { - const href = `./oss-dev/static/product.json`; - + const href = FileAccess.asBrowserUri('vs/../../product.json').toString(true); try { - var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("GET", href, false); + const xmlhttp = new XMLHttpRequest(); + xmlhttp.open('GET', href, false); xmlhttp.send(); - if (xmlhttp.status == 200 && xmlhttp.readyState == 4) { + if (xmlhttp.status === 200 && xmlhttp.readyState === 4) { return JSON.parse(xmlhttp.responseText); }