From 056fa7ada096f5492eef003cbe5554d0c9c9babb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 6 Sep 2024 09:00:38 -0700 Subject: [PATCH 01/45] Add Chat symbol anchor proposal For #227753 Adds the first part of #227753, which lets `ChatResponseAnchorPart` show a symbol. This enables nicer rendering of the anchor --- src/vs/base/browser/ui/list/listWidget.ts | 4 +- .../common/extensionsApiProposals.ts | 3 + .../api/common/extHostTypeConverters.ts | 13 +++- src/vs/workbench/api/common/extHostTypes.ts | 8 +-- .../chatMarkdownDecorationsRenderer.ts | 65 ++++++++++--------- .../browser/media/chatInlineAnchorWidget.css | 46 +++++++++++++ .../media/chatInlineFileLinkWidget.css | 12 ---- .../contrib/chat/common/annotations.ts | 27 +++++++- .../contrib/chat/common/chatModel.ts | 7 +- .../contrib/chat/common/chatService.ts | 3 +- .../vscode.proposed.chatSymbolAnchor.d.ts | 19 ++++++ 11 files changed, 153 insertions(+), 54 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css delete mode 100644 src/vs/workbench/contrib/chat/browser/media/chatInlineFileLinkWidget.css create mode 100644 src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 67b8e18a3a6..6b6f6bcdd6a 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -892,7 +892,7 @@ export class DefaultStyleController implements IStyleController { } if (styles.listActiveSelectionIconForeground) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected .codicon { color: ${styles.listActiveSelectionIconForeground}; }`); + content.push(`@layer monaco-list { .monaco-list${suffix}:focus .monaco-list-row.selected .codicon { color: ${styles.listActiveSelectionIconForeground}; } }`); } if (styles.listFocusAndSelectionBackground) { @@ -915,7 +915,7 @@ export class DefaultStyleController implements IStyleController { } if (styles.listInactiveSelectionIconForeground) { - content.push(`.monaco-list${suffix} .monaco-list-row.focused .codicon { color: ${styles.listInactiveSelectionIconForeground}; }`); + content.push(`@layer monaco-list { .monaco-list${suffix} .monaco-list-row.focused .codicon { color: ${styles.listInactiveSelectionIconForeground}; } }`); } if (styles.listInactiveFocusBackground) { diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 362b4298cdd..ca51f6dbf68 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -40,6 +40,9 @@ const _allApiProposals = { chatProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts', }, + chatSymbolAnchor: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts', + }, chatTab: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts', }, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index b71c0f98796..c0652cccd45 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2486,18 +2486,27 @@ export namespace ChatResponseAnchorPart { export function from(part: vscode.ChatResponseAnchorPart): Dto { // Work around type-narrowing confusion between vscode.Uri and URI const isUri = (thing: unknown): thing is vscode.Uri => URI.isUri(thing); + const isSymbolInformation = (x: any): x is vscode.SymbolInformation => x instanceof types.SymbolInformation; return { kind: 'inlineReference', name: part.title, - inlineReference: isUri(part.value) ? part.value : Location.from(part.value) + inlineReference: isUri(part.value) + ? part.value + : isSymbolInformation(part.value) + ? WorkspaceSymbol.from(part.value) + : Location.from(part.value) }; } export function to(part: Dto): vscode.ChatResponseAnchorPart { const value = revive(part); return new types.ChatResponseAnchorPart( - URI.isUri(value.inlineReference) ? value.inlineReference : Location.to(value.inlineReference), + URI.isUri(value.inlineReference) + ? value.inlineReference + : 'location' in value.inlineReference + ? WorkspaceSymbol.to(value.inlineReference) + : Location.to(value.inlineReference), part.name ); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 842dbbf5679..c2e1d7a38e5 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1346,14 +1346,14 @@ export class SymbolInformation { location!: Location; kind: SymbolKind; tags?: SymbolTag[]; - containerName: string | undefined; + containerName: string; constructor(name: string, kind: SymbolKind, containerName: string | undefined, location: Location); constructor(name: string, kind: SymbolKind, range: Range, uri?: URI, containerName?: string); constructor(name: string, kind: SymbolKind, rangeOrContainer: string | undefined | Range, locationOrUri?: Location | URI, containerName?: string) { this.name = name; this.kind = kind; - this.containerName = containerName; + this.containerName = containerName ?? ''; if (typeof rangeOrContainer === 'string') { this.containerName = rangeOrContainer; @@ -4422,9 +4422,9 @@ export class ChatResponseFileTreePart { } export class ChatResponseAnchorPart { - value: vscode.Uri | vscode.Location; + value: vscode.Uri | vscode.Location | vscode.SymbolInformation; title?: string; - constructor(value: vscode.Uri | vscode.Location, title?: string) { + constructor(value: vscode.Uri | vscode.Location | vscode.SymbolInformation, title?: string) { this.value = value; this.title = title; } diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index d3e17a2ade9..95a0959888a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { applyDragImage } from '../../../../base/browser/dnd.js'; import * as dom from '../../../../base/browser/dom.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; @@ -13,7 +12,8 @@ import { Lazy } from '../../../../base/common/lazy.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { revive } from '../../../../base/common/marshalling.js'; import { URI } from '../../../../base/common/uri.js'; -import { Location } from '../../../../editor/common/languages.js'; +import { IRange } from '../../../../editor/common/core/range.js'; +import { SymbolKinds } from '../../../../editor/common/languages.js'; import { ILanguageService } from '../../../../editor/common/languages/language.js'; import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; import { IModelService } from '../../../../editor/common/services/model.js'; @@ -23,11 +23,9 @@ import { IInstantiationService, ServicesAccessor } from '../../../../platform/in import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { ILogService } from '../../../../platform/log/common/log.js'; -import { listActiveSelectionBackground, listActiveSelectionForeground } from '../../../../platform/theme/common/colors/listColors.js'; import { asCssVariable } from '../../../../platform/theme/common/colorUtils.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { fillEditorsDragData } from '../../../browser/dnd.js'; -import { contentRefUrl } from '../common/annotations.js'; +import { ContentRefData, contentRefUrl } from '../common/annotations.js'; import { getFullyQualifiedId, IChatAgentCommand, IChatAgentData, IChatAgentNameService, IChatAgentService } from '../common/chatAgents.js'; import { chatSlashCommandBackground, chatSlashCommandForeground } from '../common/chatColors.js'; import { chatAgentLeader, ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestDynamicVariablePart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestToolPart, ChatRequestVariablePart, chatSubcommandLeader, IParsedChatRequest, IParsedChatRequestPart } from '../common/chatParserTypes.js'; @@ -36,7 +34,7 @@ import { IChatVariablesService } from '../common/chatVariables.js'; import { ILanguageModelToolsService } from '../common/languageModelToolsService.js'; import { IChatWidgetService } from './chat.js'; import { ChatAgentHover, getChatAgentHoverOptions } from './chatAgentHover.js'; -import './media/chatInlineFileLinkWidget.css'; +import './media/chatInlineAnchorWidget.css'; /** For rendering slash commands, variables */ const decorationRefUrl = `http://_vscodedecoration_`; @@ -241,20 +239,20 @@ export class ChatMarkdownDecorationsRenderer extends Disposable { private renderFileWidget(href: string, a: HTMLAnchorElement, store: DisposableStore): void { // TODO this can be a nicer FileLabel widget with an icon. Do a simple link for now. const fullUri = URI.parse(href); - let location: Location | { uri: URI; range: undefined }; + let data: ContentRefData; try { - location = revive(JSON.parse(fullUri.fragment)); + data = revive(JSON.parse(fullUri.fragment)); } catch (err) { this.logService.error('Invalid chat widget render data JSON', toErrorMessage(err)); return; } - if (!location.uri || !URI.isUri(location.uri)) { + if (data.kind !== 'symbol' && !URI.isUri(data.uri)) { this.logService.error(`Invalid chat widget render data: ${fullUri.fragment}`); return; } - store.add(this.instantiationService.createInstance(InlineFileLinkWidget, a, location)); + store.add(this.instantiationService.createInstance(InlineAnchorWidget, a, data)); } private renderResourceWidget(name: string, args: IDecorationWidgetArgs | undefined, store: DisposableStore): HTMLElement { @@ -284,47 +282,56 @@ export class ChatMarkdownDecorationsRenderer extends Disposable { } -class InlineFileLinkWidget extends Disposable { +class InlineAnchorWidget extends Disposable { constructor( element: HTMLAnchorElement, - location: Location | { uri: URI; range: undefined }, + data: ContentRefData, @IHoverService hoverService: IHoverService, @IInstantiationService instantiationService: IInstantiationService, @ILabelService labelService: ILabelService, @ILanguageService languageService: ILanguageService, @IModelService modelService: IModelService, - @IThemeService themeService: IThemeService, ) { super(); - element.classList.add('chat-inline-file-link-widget'); + element.classList.add('chat-inline-anchor-widget', 'show-file-icons'); + element.replaceChildren(); + + const resourceLabel = this._register(new IconLabel(element, { supportHighlights: false, supportIcons: true })); + + let location: { readonly uri: URI; readonly range?: IRange }; + if (data.kind === 'symbol') { + location = data.symbol.location; + + const icon = SymbolKinds.toIcon(data.symbol.kind); + resourceLabel.setLabel(`$(${icon.id}) ${data.symbol.name}`, undefined, {}); + } else { + location = data; + + const label = labelService.getUriBasenameLabel(location.uri); + const title = location.range && data.kind !== 'symbol' ? + `${label}#${location.range.startLineNumber}-${location.range.endLineNumber}` : + label; + + resourceLabel.setLabel(title, undefined, { + extraClasses: getIconClasses(modelService, languageService, location.uri) + }); + } const fragment = location.range ? `${location.range.startLineNumber}-${location.range.endLineNumber}` : ''; element.setAttribute('data-href', location.uri.with({ fragment }).toString()); - const label = labelService.getUriLabel(location.uri, { relative: true }); - const title = location.range ? - `${label}#${location.range.startLineNumber}-${location.range.endLineNumber}` : - label; - - element.replaceChildren(); - - const resourceLabel = this._register(new IconLabel(element, { supportHighlights: false, supportIcons: true })); - resourceLabel.setLabel(label, undefined, { - extraClasses: getIconClasses(modelService, languageService, location.uri) - }); - // Hover - this._register(hoverService.setupManagedHover(getDefaultHoverDelegate('element'), element, title)); + const relativeLabel = labelService.getUriLabel(location.uri, { relative: true }); + this._register(hoverService.setupManagedHover(getDefaultHoverDelegate('element'), element, relativeLabel)); // Drag and drop element.draggable = true; this._register(dom.addDisposableListener(element, 'dragstart', e => { instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, [location.uri], e)); - const theme = themeService.getColorTheme(); - applyDragImage(e, label, 'monaco-drag-image', theme.getColor(listActiveSelectionBackground)?.toString(), theme.getColor(listActiveSelectionForeground)?.toString()); + e.dataTransfer?.setDragImage(element, 0, 0); })); } } diff --git a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css new file mode 100644 index 00000000000..cd4255fcefe --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.chat-inline-anchor-widget, +.chat-inline-anchor-widget a { + color: inherit !important; +} + +.chat-inline-anchor-widget { + outline: 1px solid var(--vscode-chat-requestBorder, var(--vscode-input-background, transparent)); + outline-offset: -1px; + border-radius: 4px; +} + +.chat-inline-anchor-widget .monaco-icon-label { + display: inline-flex; + margin: 0 1px; + vertical-align: text-bottom; + align-items: center; + line-height: normal; +} + +.chat-inline-anchor-widget .monaco-highlighted-label { + padding: 2px; + padding-right: 4px; +} + +.chat-inline-anchor-widget .codicon { + vertical-align: text-bottom; + color: reset-layer !important; +} + +.chat-inline-anchor-widget:hover .monaco-icon-label { + background-color: var(--vscode-list-hoverBackground); +} + +.chat-inline-anchor-widget .monaco-icon-label::before { + height: auto; + padding-right: 3px; +} + +.chat-inline-anchor-widget .monaco-icon-label .monaco-highlighted-label { + white-space: normal; +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chatInlineFileLinkWidget.css b/src/vs/workbench/contrib/chat/browser/media/chatInlineFileLinkWidget.css deleted file mode 100644 index d4746e3afe3..00000000000 --- a/src/vs/workbench/contrib/chat/browser/media/chatInlineFileLinkWidget.css +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.chat-inline-file-link-widget .monaco-icon-label { - display: inline-flex; -} - -.chat-inline-file-link-widget .monaco-icon-label .monaco-highlighted-label { - white-space: normal; -} diff --git a/src/vs/workbench/contrib/chat/common/annotations.ts b/src/vs/workbench/contrib/chat/common/annotations.ts index 8aede0dc32b..5d0fd7db153 100644 --- a/src/vs/workbench/contrib/chat/common/annotations.ts +++ b/src/vs/workbench/contrib/chat/common/annotations.ts @@ -6,19 +6,42 @@ import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { basename } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { IRange } from '../../../../editor/common/core/range.js'; +import { IWorkspaceSymbol } from '../../search/common/search.js'; import { IChatProgressRenderableResponseContent, IChatProgressResponseContent, appendMarkdownString, canMergeMarkdownStrings } from './chatModel.js'; import { IChatAgentVulnerabilityDetails, IChatMarkdownContent } from './chatService.js'; export const contentRefUrl = 'http://_vscodecontentref_'; // must be lowercase for URI +export type ContentRefData = + | { readonly kind: 'symbol'; readonly symbol: IWorkspaceSymbol } + | { + readonly kind?: undefined; + readonly uri: URI; + readonly range?: IRange; + }; + export function annotateSpecialMarkdownContent(response: ReadonlyArray): IChatProgressRenderableResponseContent[] { const result: IChatProgressRenderableResponseContent[] = []; for (const item of response) { const previousItem = result[result.length - 1]; if (item.kind === 'inlineReference') { - const location = 'uri' in item.inlineReference ? item.inlineReference : { uri: item.inlineReference }; + const location: ContentRefData = 'uri' in item.inlineReference + ? item.inlineReference + : 'name' in item.inlineReference + ? { kind: 'symbol', symbol: item.inlineReference } + : { uri: item.inlineReference }; + const printUri = URI.parse(contentRefUrl).with({ fragment: JSON.stringify(location) }); - const markdownText = `[${item.name || basename(location.uri)}](${printUri.toString()})`; + let label: string | undefined = item.name; + if (!label) { + if (location.kind === 'symbol') { + label = location.symbol.name; + } else { + label = basename(location.uri); + } + } + + const markdownText = `[${label}](${printUri.toString()})`; if (previousItem?.kind === 'markdownContent') { const merged = appendMarkdownString(previousItem.content, new MarkdownString(markdownText)); result[result.length - 1] = { content: merged, kind: 'markdownContent' }; diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 3952609480e..49535f0cc9c 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -289,11 +289,14 @@ 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 basename('uri' in part.inlineReference ? part.inlineReference.uri : part.inlineReference); + return inlineRefToRepr(part); } else if (part.kind === 'command') { return part.command.title; } else if (part.kind === 'textEditGroup') { @@ -313,7 +316,7 @@ export class Response extends Disposable implements IResponse { this._markdownContent = this._responseParts.map(part => { if (part.kind === 'inlineReference') { - return basename('uri' in part.inlineReference ? part.inlineReference.uri : part.inlineReference); + return inlineRefToRepr(part); } else if (part.kind === 'markdownContent' || part.kind === 'markdownVuln') { return part.content.value; } else { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index abb04d4a0bf..99edf364e6f 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -14,6 +14,7 @@ import { ISelection } from '../../../../editor/common/core/selection.js'; import { Command, Location, TextEdit } from '../../../../editor/common/languages.js'; import { FileType } from '../../../../platform/files/common/files.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { IWorkspaceSymbol } from '../../search/common/search.js'; import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentResult } from './chatAgents.js'; import { ChatModel, IChatModel, IChatRequestModel, IChatRequestVariableData, IChatRequestVariableEntry, IChatResponseModel, IExportableChatData, ISerializableChatData } from './chatModel.js'; import { IParsedChatRequest } from './chatParserTypes.js'; @@ -96,7 +97,7 @@ export interface IChatCodeCitation { } export interface IChatContentInlineReference { - inlineReference: URI | Location; + inlineReference: URI | Location | IWorkspaceSymbol; name?: string; kind: 'inlineReference'; } diff --git a/src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts b/src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts new file mode 100644 index 00000000000..487a41e2956 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * 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' { + + // Extended to add `SymbolInformation`. Would also be added to constructor + export interface ChatResponseAnchorPart { + /** + * The target of this anchor. + * + * If this is a {@linkcode Uri} or {@linkcode Location}, this is rendered as a normal link. + * + * If this is a {@linkcode SymbolInformation}, this is rendered as a symbol link. + */ + value: Uri | Location | SymbolInformation; + } +} From e8e4e8742d77bf8227d8433f1fe639d97460eab8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 6 Sep 2024 16:17:21 -0700 Subject: [PATCH 02/45] Combine proposals Also hooks up some experimental context menu actions --- src/vs/platform/actions/common/actions.ts | 2 + .../common/extensionsApiProposals.ts | 3 - src/vs/workbench/api/common/extHostTypes.ts | 6 +- .../chat/browser/chatInlineAnchorWidget.ts | 203 ++++++++++++++++++ .../chatMarkdownDecorationsRenderer.ts | 63 +----- .../files/browser/fileActions.contribution.ts | 24 +++ ...ode.proposed.chatParticipantAdditions.d.ts | 12 ++ .../vscode.proposed.chatSymbolAnchor.d.ts | 19 -- 8 files changed, 246 insertions(+), 86 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts delete mode 100644 src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 62d68f28f16..3fcecbff639 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -222,6 +222,8 @@ export class MenuId { static readonly ChatExecute = new MenuId('ChatExecute'); static readonly ChatExecuteSecondary = new MenuId('ChatExecuteSecondary'); static readonly ChatInputSide = new MenuId('ChatInputSide'); + static readonly ChatInlineResourceAnchorContext = new MenuId('ChatInlineResourceAnchorContext'); + static readonly ChatInlineSymbolAnchorContext = new MenuId('ChatInlineSymbolAnchorContext'); static readonly AccessibleView = new MenuId('AccessibleView'); static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar'); static readonly DiffEditorHunkToolbar = new MenuId('DiffEditorHunkToolbar'); diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index ca51f6dbf68..362b4298cdd 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -40,9 +40,6 @@ const _allApiProposals = { chatProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts', }, - chatSymbolAnchor: { - proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts', - }, chatTab: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts', }, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index c2e1d7a38e5..1811501aef7 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4422,10 +4422,12 @@ export class ChatResponseFileTreePart { } export class ChatResponseAnchorPart { - value: vscode.Uri | vscode.Location | vscode.SymbolInformation; + value: vscode.Uri | vscode.Location; + value2: vscode.Uri | vscode.Location | vscode.SymbolInformation; title?: string; constructor(value: vscode.Uri | vscode.Location | vscode.SymbolInformation, title?: string) { - this.value = value; + this.value = value as any; + this.value2 = value; this.title = title; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts new file mode 100644 index 00000000000..4dfab620aff --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as dom from '../../../../base/browser/dom.js'; +import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; +import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; +import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js'; +import { IAction } from '../../../../base/common/actions.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; +import { IRange } from '../../../../editor/common/core/range.js'; +import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; +import { Location, SymbolKinds } from '../../../../editor/common/languages.js'; +import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/services/model.js'; +import { DefinitionAction } from '../../../../editor/contrib/gotoSymbol/browser/goToCommands.js'; +import * as nls from '../../../../nls.js'; +import { createAndFillInContextMenuActions } 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 { 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, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILabelService } from '../../../../platform/label/common/label.js'; +import { fillEditorsDragData } from '../../../browser/dnd.js'; +import { ContentRefData } from '../common/annotations.js'; + +export class InlineAnchorWidget extends Disposable { + + constructor( + element: HTMLAnchorElement, + data: ContentRefData, + @IHoverService hoverService: IHoverService, + @IInstantiationService instantiationService: IInstantiationService, + @ILabelService labelService: ILabelService, + @ILanguageService languageService: ILanguageService, + @IModelService modelService: IModelService, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService originalContextKeyService: IContextKeyService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, + @IMenuService menuService: IMenuService, + ) { + super(); + + const contextKeyService = this._register(originalContextKeyService.createScoped(element)); + + element.classList.add('chat-inline-anchor-widget', 'show-file-icons'); + element.replaceChildren(); + + const resourceLabel = this._register(new IconLabel(element, { supportHighlights: false, supportIcons: true })); + + let location: { readonly uri: URI; readonly range?: IRange }; + let contextMenuId: MenuId; + if (data.kind === 'symbol') { + location = data.symbol.location; + contextMenuId = MenuId.ChatInlineSymbolAnchorContext; + + const icon = SymbolKinds.toIcon(data.symbol.kind); + resourceLabel.setLabel(`$(${icon.id}) ${data.symbol.name}`, undefined, {}); + + const model = modelService.getModel(location.uri); + if (model) { + const hasDefinitionProvider = EditorContextKeys.hasDefinitionProvider.bindTo(contextKeyService); + const hasReferenceProvider = EditorContextKeys.hasReferenceProvider.bindTo(contextKeyService); + const updateContents = () => { + if (model.isDisposed()) { + return; + } + + hasDefinitionProvider.set(languageFeaturesService.definitionProvider.has(model)); + hasReferenceProvider.set(languageFeaturesService.definitionProvider.has(model)); + }; + updateContents(); + this._register(languageFeaturesService.definitionProvider.onDidChange(updateContents)); + this._register(languageFeaturesService.referenceProvider.onDidChange(updateContents)); + } + } else { + location = data; + contextMenuId = MenuId.ChatInlineResourceAnchorContext; + + const label = labelService.getUriBasenameLabel(location.uri); + const title = location.range && data.kind !== 'symbol' ? + `${label}#${location.range.startLineNumber}-${location.range.endLineNumber}` : + label; + + resourceLabel.setLabel(title, undefined, { + extraClasses: getIconClasses(modelService, languageService, location.uri) + }); + } + + const fragment = location.range ? `${location.range.startLineNumber}-${location.range.endLineNumber}` : ''; + element.setAttribute('data-href', location.uri.with({ fragment }).toString()); + + // Context menu + this._register(dom.addDisposableListener(element, dom.EventType.CONTEXT_MENU, domEvent => { + const event = new StandardMouseEvent(dom.getWindow(domEvent), domEvent); + dom.EventHelper.stop(domEvent, true); + + contextMenuService.showContextMenu({ + contextKeyService, + getAnchor: () => event, + getActions: () => { + const menu = menuService.getMenuActions(contextMenuId, contextKeyService, { arg: location }); + const primary: IAction[] = []; + createAndFillInContextMenuActions(menu, primary); + return primary; + }, + }); + })); + + // Hover + const relativeLabel = labelService.getUriLabel(location.uri, { relative: true }); + this._register(hoverService.setupManagedHover(getDefaultHoverDelegate('element'), element, relativeLabel)); + + // Drag and drop + element.draggable = true; + this._register(dom.addDisposableListener(element, 'dragstart', e => { + instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, [location.uri], e)); + + e.dataTransfer?.setDragImage(element, 0, 0); + })); + } +} + +registerAction2(class GoToDefinitionAction extends Action2 { + + static readonly id = 'chat.inlineSymbolAnchor.goToDefinition'; + + constructor() { + super({ + id: GoToDefinitionAction.id, + title: { + ...nls.localize2('actions.goToDecl.label', "Go to Definition"), + mnemonicTitle: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition"), + }, + precondition: EditorContextKeys.hasDefinitionProvider, + menu: [{ + id: MenuId.ChatInlineSymbolAnchorContext, + group: 'navigation', + order: 1.1, + },] + }); + } + + override async run(accessor: ServicesAccessor, location: Location): Promise { + const editorService = accessor.get(ICodeEditorService); + + await editorService.openCodeEditor({ + resource: location.uri, options: { + selection: { + startColumn: location.range.startColumn, + startLineNumber: location.range.startLineNumber, + } + } + }, null); + + const action = new DefinitionAction({ openToSide: false, openInPeek: false, muteMessage: true }, { title: { value: '', original: '' }, id: '', precondition: undefined }); + return action.run(accessor); + } +}); + +registerAction2(class GoToReferencesAction extends Action2 { + + static readonly id = 'chat.inlineSymbolAnchor.goToReferences'; + + constructor() { + super({ + id: GoToReferencesAction.id, + title: { + ...nls.localize2('goToReferences.label', "Go to References"), + mnemonicTitle: nls.localize({ key: 'miGotoReference', comment: ['&& denotes a mnemonic'] }, "Go to &&References"), + }, + precondition: EditorContextKeys.hasReferenceProvider, + menu: [{ + id: MenuId.ChatInlineSymbolAnchorContext, + group: 'navigation', + order: 1.1, + },] + }); + } + + override async run(accessor: ServicesAccessor, location: Location): Promise { + const editorService = accessor.get(ICodeEditorService); + const commandService = accessor.get(ICommandService); + + await editorService.openCodeEditor({ + resource: location.uri, options: { + selection: { + startColumn: location.range.startColumn, + startLineNumber: location.range.startLineNumber, + } + } + }, null); + + await commandService.executeCommand('editor.action.goToReferences'); + } +}); diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index 95a0959888a..79555a8099f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -6,17 +6,11 @@ import * as dom from '../../../../base/browser/dom.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; -import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js'; import { toErrorMessage } from '../../../../base/common/errorMessage.js'; import { Lazy } from '../../../../base/common/lazy.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { revive } from '../../../../base/common/marshalling.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { SymbolKinds } from '../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; @@ -24,7 +18,6 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { ILabelService } from '../../../../platform/label/common/label.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { asCssVariable } from '../../../../platform/theme/common/colorUtils.js'; -import { fillEditorsDragData } from '../../../browser/dnd.js'; import { ContentRefData, contentRefUrl } from '../common/annotations.js'; import { getFullyQualifiedId, IChatAgentCommand, IChatAgentData, IChatAgentNameService, IChatAgentService } from '../common/chatAgents.js'; import { chatSlashCommandBackground, chatSlashCommandForeground } from '../common/chatColors.js'; @@ -34,6 +27,7 @@ import { IChatVariablesService } from '../common/chatVariables.js'; import { ILanguageModelToolsService } from '../common/languageModelToolsService.js'; import { IChatWidgetService } from './chat.js'; import { ChatAgentHover, getChatAgentHoverOptions } from './chatAgentHover.js'; +import { InlineAnchorWidget } from './chatInlineAnchorWidget.js'; import './media/chatInlineAnchorWidget.css'; /** For rendering slash commands, variables */ @@ -280,58 +274,3 @@ export class ChatMarkdownDecorationsRenderer extends Disposable { } } } - - -class InlineAnchorWidget extends Disposable { - - constructor( - element: HTMLAnchorElement, - data: ContentRefData, - @IHoverService hoverService: IHoverService, - @IInstantiationService instantiationService: IInstantiationService, - @ILabelService labelService: ILabelService, - @ILanguageService languageService: ILanguageService, - @IModelService modelService: IModelService, - ) { - super(); - - element.classList.add('chat-inline-anchor-widget', 'show-file-icons'); - element.replaceChildren(); - - const resourceLabel = this._register(new IconLabel(element, { supportHighlights: false, supportIcons: true })); - - let location: { readonly uri: URI; readonly range?: IRange }; - if (data.kind === 'symbol') { - location = data.symbol.location; - - const icon = SymbolKinds.toIcon(data.symbol.kind); - resourceLabel.setLabel(`$(${icon.id}) ${data.symbol.name}`, undefined, {}); - } else { - location = data; - - const label = labelService.getUriBasenameLabel(location.uri); - const title = location.range && data.kind !== 'symbol' ? - `${label}#${location.range.startLineNumber}-${location.range.endLineNumber}` : - label; - - resourceLabel.setLabel(title, undefined, { - extraClasses: getIconClasses(modelService, languageService, location.uri) - }); - } - - const fragment = location.range ? `${location.range.startLineNumber}-${location.range.endLineNumber}` : ''; - element.setAttribute('data-href', location.uri.with({ fragment }).toString()); - - // Hover - const relativeLabel = labelService.getUriLabel(location.uri, { relative: true }); - this._register(hoverService.setupManagedHover(getDefaultHoverDelegate('element'), element, relativeLabel)); - - // Drag and drop - element.draggable = true; - this._register(dom.addDisposableListener(element, 'dragstart', e => { - instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, [location.uri], e)); - - e.dataTransfer?.setDragImage(element, 0, 0); - })); - } -} diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index c1b61149ad8..82c188c79a0 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -757,3 +757,27 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { }, order: 1 }); + + +// Chat resource anchor context menu + +MenuRegistry.appendMenuItem(MenuId.ChatInlineResourceAnchorContext, { + group: 'navigation', + order: 10, + command: openToSideCommand, + when: ResourceContextKey.HasResource +}); + +MenuRegistry.appendMenuItem(MenuId.ChatInlineResourceAnchorContext, { + group: '1_cutcopypaste', + order: 10, + command: copyPathCommand, + when: ResourceContextKey.IsFileSystemResource +}); + +MenuRegistry.appendMenuItem(MenuId.ChatInlineResourceAnchorContext, { + group: '1_cutcopypaste', + order: 20, + command: copyRelativePathCommand, + when: ResourceContextKey.IsFileSystemResource +}); diff --git a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts index a3ff6db82d1..9352503656d 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts @@ -129,6 +129,18 @@ declare module 'vscode' { constructor(uri: Uri, range: Range); } + // Extended to add `SymbolInformation`. Would also be added to `constructor`. + export interface ChatResponseAnchorPart { + /** + * The target of this anchor. + * + * If this is a {@linkcode Uri} or {@linkcode Location}, this is rendered as a normal link. + * + * If this is a {@linkcode SymbolInformation}, this is rendered as a symbol link. + */ + value2: Uri | Location | SymbolInformation; + } + export interface ChatResponseStream { /** diff --git a/src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts b/src/vscode-dts/vscode.proposed.chatSymbolAnchor.d.ts deleted file mode 100644 index 487a41e2956..00000000000 --- a/src/vscode-dts/vscode.proposed.chatSymbolAnchor.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. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // Extended to add `SymbolInformation`. Would also be added to constructor - export interface ChatResponseAnchorPart { - /** - * The target of this anchor. - * - * If this is a {@linkcode Uri} or {@linkcode Location}, this is rendered as a normal link. - * - * If this is a {@linkcode SymbolInformation}, this is rendered as a symbol link. - */ - value: Uri | Location | SymbolInformation; - } -} From f4739315091bb87992a0606fcf343650b71e7b1a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:31:30 +0200 Subject: [PATCH 03/45] SCM Graph - improve picker hover (#227978) --- src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index d6f366299e8..b67f54ee8e2 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -61,13 +61,12 @@ import { clamp } from '../../../../base/common/numbers.js'; import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; import { structuralEquals } from '../../../../base/common/equals.js'; import { compare } from '../../../../base/common/strings.js'; -import { createInstantHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; type TreeElement = SCMHistoryItemViewModelTreeElement | SCMHistoryItemLoadMoreTreeElement; class SCMRepositoryActionViewItem extends ActionViewItem { constructor(private readonly _repository: ISCMRepository, action: IAction, options?: IDropdownMenuActionViewItemOptions) { - super(null, action, { ...options, icon: false, label: true, hoverDelegate: createInstantHoverDelegate() }); + super(null, action, { ...options, icon: false, label: true }); } protected override updateLabel(): void { @@ -97,7 +96,7 @@ class SCMHistoryItemRefsActionViewItem extends ActionViewItem { action: IAction, options?: IDropdownMenuActionViewItemOptions ) { - super(null, action, { ...options, icon: false, label: true, hoverDelegate: createInstantHoverDelegate() }); + super(null, action, { ...options, icon: false, label: true }); } protected override updateLabel(): void { From 6b90336e96bf90354246ebe53922cf639486305e Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:33:44 +0200 Subject: [PATCH 04/45] SCM Graph - add "Outdated" badge to view title (#227996) --- .../contrib/scm/browser/media/scm.css | 14 +++++++ .../contrib/scm/browser/scmHistoryViewPane.ts | 37 +++++++++++++++---- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 81c5fd8ec42..1e6e2a8d9e2 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -491,6 +491,20 @@ /* Graph */ +.pane-header .scm-graph-view-badge-container { + display: flex; + align-items: center; + min-width: fit-content; +} + +.pane-header .scm-graph-view-badge-container > .scm-graph-view-badge.monaco-count-badge.long { + background-color: var(--vscode-badge-background); + color: var(--vscode-badge-foreground); + border: 1px solid var(--vscode-contrastBorder); + margin-left: 6px; + padding: 2px 4px; +} + .monaco-workbench .part.auxiliarybar > .title > .title-actions .action-label.scm-graph-repository-picker { display: flex; } diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index b67f54ee8e2..9abd6a20719 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -980,7 +980,9 @@ export class SCMHistoryViewPane extends ViewPane { private _treeViewModel!: SCMHistoryViewModel; private _treeDataSource!: SCMHistoryTreeDataSource; private _treeIdentityProvider!: SCMHistoryTreeIdentityProvider; - private _repositoryLoadMore = observableValue(this, false); + + private readonly _repositoryLoadMore = observableValue(this, false); + private readonly _repositoryOutdated = observableValue(this, false); private readonly _actionRunner: IActionRunner; private readonly _visibilityDisposables = new DisposableStore(); @@ -1021,9 +1023,20 @@ export class SCMHistoryViewPane extends ViewPane { this._register(this._updateChildrenThrottler); } - protected override layoutBody(height: number, width: number): void { - super.layoutBody(height, width); - this._tree.layout(height, width); + protected override renderHeaderTitle(container: HTMLElement): void { + super.renderHeaderTitle(container, this.title); + + const element = h('div.scm-graph-view-badge-container', [ + h('div.scm-graph-view-badge.monaco-count-badge.long@badge') + ]); + + element.badge.textContent = 'Outdated'; + container.appendChild(element.root); + + this._register(autorun(reader => { + const outdated = this._repositoryOutdated.read(reader); + element.root.style.display = outdated ? '' : 'none'; + })); } protected override renderBody(container: HTMLElement): void { @@ -1105,7 +1118,10 @@ export class SCMHistoryViewPane extends ViewPane { return true; }, }, (reader, changeSummary) => { - if ((!historyItemGroup.read(reader) && !historyItemRemoteRevision.read(reader)) || changeSummary.refresh === false) { + const historyItemGroupValue = historyItemGroup.read(reader); + const historyItemRemoteRevisionValue = historyItemRemoteRevision.read(reader); + + if ((!historyItemGroupValue && !historyItemRemoteRevisionValue) || changeSummary.refresh === false) { return; } @@ -1123,8 +1139,8 @@ export class SCMHistoryViewPane extends ViewPane { return; } - // Set the "OUTDATED" description - this.updateTitleDescription(localize('outdated', "OUTDATED")); + // Show the "Outdated" badge on the view + this._repositoryOutdated.set(true, undefined); } })); @@ -1144,6 +1160,11 @@ export class SCMHistoryViewPane extends ViewPane { }); } + protected override layoutBody(height: number, width: number): void { + super.layoutBody(height, width); + this._tree.layout(height, width); + } + override getActionRunner(): IActionRunner | undefined { return this._actionRunner; } @@ -1173,7 +1194,7 @@ export class SCMHistoryViewPane extends ViewPane { await this._updateChildren(true); this.updateActions(); - this.updateTitleDescription(undefined); + this._repositoryOutdated.set(false, undefined); this._tree.scrollTop = 0; } From 70849c674dfe2ced68a9bee7b556126a3b23439f Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:10:48 +0200 Subject: [PATCH 05/45] =?UTF-8?q?SCM=20Graph=20-=20=F0=9F=92=84=20switch?= =?UTF-8?q?=20to=20waitForState=20in=20favor=20of=20an=20autorun=20(#22800?= =?UTF-8?q?0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/scm/browser/scmHistoryViewPane.ts | 160 +++++++++--------- 1 file changed, 78 insertions(+), 82 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 9abd6a20719..ea5a76d79f6 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -16,7 +16,7 @@ import { fromNow } from '../../../../base/common/date.js'; import { createMatches, FuzzyScore, IMatch } from '../../../../base/common/filters.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; -import { autorun, autorunWithStore, autorunWithStoreHandleChanges, derived, derivedOpts, IObservable, observableValue } from '../../../../base/common/observable.js'; +import { autorun, autorunWithStoreHandleChanges, derived, derivedOpts, IObservable, observableValue, waitForState } from '../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -48,7 +48,7 @@ import { ActionRunner, IAction, IActionRunner } from '../../../../base/common/ac import { delta, groupBy, tail } from '../../../../base/common/arrays.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { IProgressService } from '../../../../platform/progress/common/progress.js'; -import { constObservable, derivedConstOnceDefined, latestChangedValue, observableFromEvent, runOnChange } from '../../../../base/common/observableInternal/utils.js'; +import { constObservable, latestChangedValue, observableFromEvent, runOnChange } from '../../../../base/common/observableInternal/utils.js'; import { ContextKeys } from './scmViewPane.js'; import { IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IDropdownMenuActionViewItemOptions } from '../../../../base/browser/ui/dropdown/dropdownActionViewItem.js'; @@ -1047,12 +1047,12 @@ export class SCMHistoryViewPane extends ViewPane { this._createTree(this._treeContainer); - this.onDidChangeBodyVisibility(visible => { + this.onDidChangeBodyVisibility(async visible => { if (visible) { this._treeViewModel = this.instantiationService.createInstance(SCMHistoryViewModel); this._visibilityDisposables.add(this._treeViewModel); - const firstRepository = derivedConstOnceDefined(this, reader => { + const firstRepositoryInitialized = derived(this, reader => { const repository = this._treeViewModel.repository.read(reader); const historyProvider = repository?.provider.historyProvider.read(reader); const currentHistoryItemGroup = historyProvider?.currentHistoryItemGroup.read(reader); @@ -1060,100 +1060,96 @@ export class SCMHistoryViewPane extends ViewPane { return currentHistoryItemGroup !== undefined ? repository : undefined; }); - this._visibilityDisposables.add(autorunWithStore(async (reader, store) => { - const repository = firstRepository.read(reader); - if (!repository) { - return; - } + // Wait for first repository to be initialized + await waitForState(firstRepositoryInitialized); - this._treeOperationSequencer.queue(async () => { - await this._tree.setInput(this._treeViewModel); - this._tree.scrollTop = 0; - }); + this._treeOperationSequencer.queue(async () => { + await this._tree.setInput(this._treeViewModel); + this._tree.scrollTop = 0; + }); - // Repository change - store.add( - autorunWithStoreHandleChanges<{ refresh: boolean }>({ + // Repository change + this._visibilityDisposables.add( + autorunWithStoreHandleChanges<{ refresh: boolean }>({ + owner: this, + createEmptyChangeSummary: () => ({ refresh: false }), + handleChange(_, changeSummary) { + changeSummary.refresh = true; + return true; + }, + }, (reader, changeSummary, store) => { + const repository = this._treeViewModel.repository.read(reader); + const historyProvider = repository?.provider.historyProvider.read(reader); + if (!repository || !historyProvider) { + return; + } + + // Update context + this._scmProviderCtx.set(repository.provider.contextValue); + + // Checkout, Commit, and Publish + const historyItemGroup = derivedOpts<{ id: string; revision?: string; remoteId?: string } | undefined>({ owner: this, - createEmptyChangeSummary: () => ({ refresh: false }), - handleChange(_, changeSummary) { - changeSummary.refresh = true; - return true; - }, - }, (reader, changeSummary, store) => { - const repository = this._treeViewModel.repository.read(reader); - const historyProvider = repository?.provider.historyProvider.read(reader); - if (!repository || !historyProvider) { - return; - } + equalsFn: structuralEquals + }, reader => { + const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup.read(reader); + return currentHistoryItemGroup ? { + id: currentHistoryItemGroup.id, + revision: currentHistoryItemGroup.revision, + remoteId: currentHistoryItemGroup.remote?.id + } : undefined; + }); - // Update context - this._scmProviderCtx.set(repository.provider.contextValue); + // Fetch, Push + const historyItemRemoteRevision = derived(reader => { + return historyProvider.currentHistoryItemGroup.read(reader)?.remote?.revision; + }); - // Checkout, Commit, and Publish - const historyItemGroup = derivedOpts<{ id: string; revision?: string; remoteId?: string } | undefined>({ + // HistoryItemGroup change + store.add( + autorunWithStoreHandleChanges<{ refresh: boolean | 'ifScrollTop' }>({ owner: this, - equalsFn: structuralEquals - }, reader => { - const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup.read(reader); - return currentHistoryItemGroup ? { - id: currentHistoryItemGroup.id, - revision: currentHistoryItemGroup.revision, - remoteId: currentHistoryItemGroup.remote?.id - } : undefined; - }); + createEmptyChangeSummary: () => ({ refresh: false }), + handleChange(context, changeSummary) { + changeSummary.refresh = context.didChange(historyItemRemoteRevision) ? 'ifScrollTop' : true; + return true; + }, + }, (reader, changeSummary) => { + const historyItemGroupValue = historyItemGroup.read(reader); + const historyItemRemoteRevisionValue = historyItemRemoteRevision.read(reader); - // Fetch, Push - const historyItemRemoteRevision = derived(reader => { - return historyProvider.currentHistoryItemGroup.read(reader)?.remote?.revision; - }); + if ((!historyItemGroupValue && !historyItemRemoteRevisionValue) || changeSummary.refresh === false) { + return; + } - // HistoryItemGroup change - store.add( - autorunWithStoreHandleChanges<{ refresh: boolean | 'ifScrollTop' }>({ - owner: this, - createEmptyChangeSummary: () => ({ refresh: false }), - handleChange(context, changeSummary) { - changeSummary.refresh = context.didChange(historyItemRemoteRevision) ? 'ifScrollTop' : true; - return true; - }, - }, (reader, changeSummary) => { - const historyItemGroupValue = historyItemGroup.read(reader); - const historyItemRemoteRevisionValue = historyItemRemoteRevision.read(reader); + if (changeSummary.refresh === true) { + this.refresh(); + return; + } - if ((!historyItemGroupValue && !historyItemRemoteRevisionValue) || changeSummary.refresh === false) { - return; - } - - if (changeSummary.refresh === true) { + if (changeSummary.refresh === 'ifScrollTop') { + // Remote revision changes can occur as a result of a user action (Fetch, Push) but + // it can also occur as a result of background action (Auto Fetch). If the tree is + // scrolled to the top, we can safely refresh the tree. + if (this._tree.scrollTop === 0) { this.refresh(); return; } - if (changeSummary.refresh === 'ifScrollTop') { - // Remote revision changes can occur as a result of a user action (Fetch, Push) but - // it can also occur as a result of background action (Auto Fetch). If the tree is - // scrolled to the top, we can safely refresh the tree. - if (this._tree.scrollTop === 0) { - this.refresh(); - return; - } - - // Show the "Outdated" badge on the view - this._repositoryOutdated.set(true, undefined); - } - })); - - // HistoryItemRefs filter changed - store.add(runOnChange(this._treeViewModel.historyItemsFilter, () => { - this.refresh(); + // Show the "Outdated" badge on the view + this._repositoryOutdated.set(true, undefined); + } })); - if (changeSummary.refresh) { - this.refresh(); - } + // HistoryItemRefs filter changed + store.add(runOnChange(this._treeViewModel.historyItemsFilter, () => { + this.refresh(); })); - })); + + if (changeSummary.refresh) { + this.refresh(); + } + })); } else { this._visibilityDisposables.clear(); } From 4d221c6b85608abf3df9e6c6ac91ffb3285dff78 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 9 Sep 2024 08:52:50 -0700 Subject: [PATCH 06/45] cli: reapply "code server-web when offline" --- cli/Cargo.toml | 1 + cli/src/auth.rs | 2 +- cli/src/commands/serve_web.rs | 73 ++++++++++++++++++++++++++++------ cli/src/download_cache.rs | 8 +++- cli/src/tunnels/code_server.rs | 2 +- package.json | 2 +- 6 files changed, 71 insertions(+), 17 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b820ffcc50f..2907ff3d7e7 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -81,4 +81,5 @@ codegen-units = 1 [features] default = [] +vsda = [] vscode-encrypt = [] diff --git a/cli/src/auth.rs b/cli/src/auth.rs index 2d9162c5483..51942c96c75 100644 --- a/cli/src/auth.rs +++ b/cli/src/auth.rs @@ -723,7 +723,7 @@ impl Auth { match &init_code_json.message { Some(m) => self.log.result(m), - None => self.log.result(&format!( + None => self.log.result(format!( "To grant access to the server, please log into {} and use code {}", init_code_json.verification_uri, init_code_json.user_code )), diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs index d8d2a49bb1a..4acb9a18c73 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -15,7 +15,7 @@ use std::time::{Duration, Instant}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Request, Response, Server}; use tokio::io::{AsyncBufReadExt, BufReader}; -use tokio::pin; +use tokio::{pin, time}; use crate::async_pipe::{ get_socket_name, get_socket_rw_stream, listen_socket_rw_stream, AsyncPipe, @@ -50,7 +50,7 @@ const SERVER_IDLE_TIMEOUT_SECS: u64 = 60 * 60; /// (should be large enough to basically never happen) const SERVER_ACTIVE_TIMEOUT_SECS: u64 = SERVER_IDLE_TIMEOUT_SECS * 24 * 30 * 12; /// How long to cache the "latest" version we get from the update service. -const RELEASE_CACHE_SECS: u64 = 60 * 60; +const RELEASE_CHECK_INTERVAL: u64 = 60 * 60; /// Number of bytes for the secret keys. See workbench.ts for their usage. const SECRET_KEY_BYTES: usize = 32; @@ -86,7 +86,11 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result = ConnectionManager::new(&ctx, platform, args.clone()); + let update_check_interval = 3600; + cm.clone() + .start_update_checker(Duration::from_secs(update_check_interval)); + let key = get_server_key_half(&ctx.paths); let make_svc = move || { let ctx = HandleContext { @@ -175,7 +179,7 @@ async fn handle_proxied(ctx: &HandleContext, req: Request) -> Response r, Err(e) => { error!(ctx.log, "error getting latest version: {}", e); @@ -538,21 +542,67 @@ impl ConnectionManager { pub fn new(ctx: &CommandContext, platform: Platform, args: ServeWebArgs) -> Arc { let base_path = normalize_base_path(args.server_base_path.as_deref().unwrap_or_default()); + let cache = DownloadCache::new(ctx.paths.web_server_storage()); + let target_kind = TargetKind::Web; + + let quality = VSCODE_CLI_QUALITY.map_or(Quality::Stable, |q| match Quality::try_from(q) { + Ok(q) => q, + Err(_) => Quality::Stable, + }); + + let latest_version = tokio::sync::Mutex::new(cache.get().first().map(|latest_commit| { + ( + Instant::now() - Duration::from_secs(RELEASE_CHECK_INTERVAL), + Release { + name: String::from("0.0.0"), // Version information not stored on cache + commit: latest_commit.clone(), + platform, + target: target_kind, + quality, + }, + ) + })); + Arc::new(Self { platform, args, base_path, log: ctx.log.clone(), - cache: DownloadCache::new(ctx.paths.web_server_storage()), + cache, update_service: UpdateService::new( ctx.log.clone(), Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())), ), state: ConnectionStateMap::default(), - latest_version: tokio::sync::Mutex::default(), + latest_version, }) } + // spawns a task that checks for updates every n seconds duration + pub fn start_update_checker(self: Arc, duration: Duration) { + tokio::spawn(async move { + let mut interval = time::interval(duration); + loop { + interval.tick().await; + + if let Err(e) = self.get_latest_release().await { + warning!(self.log, "error getting latest version: {}", e); + } + } + }); + } + + // Returns the latest release from the cache, if one exists. + pub async fn get_release_from_cache(&self) -> Result { + let latest = self.latest_version.lock().await; + if let Some((_, release)) = &*latest { + return Ok(release.clone()); + } + + drop(latest); + self.get_latest_release().await + } + /// Gets a connection to a server version pub async fn get_connection( &self, @@ -571,11 +621,7 @@ impl ConnectionManager { pub async fn get_latest_release(&self) -> Result { let mut latest = self.latest_version.lock().await; let now = Instant::now(); - if let Some((checked_at, release)) = &*latest { - if checked_at.elapsed() < Duration::from_secs(RELEASE_CACHE_SECS) { - return Ok(release.clone()); - } - } + let target_kind = TargetKind::Web; let quality = VSCODE_CLI_QUALITY .ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality")) @@ -585,13 +631,14 @@ impl ConnectionManager { let release = self .update_service - .get_latest_commit(self.platform, TargetKind::Web, quality) + .get_latest_commit(self.platform, target_kind, quality) .await .map_err(|e| CodeError::UpdateCheckFailed(e.to_string())); // If the update service is unavailable and we have stale data, use that - if let (Err(e), Some((_, previous))) = (&release, &*latest) { + if let (Err(e), Some((_, previous))) = (&release, latest.clone()) { warning!(self.log, "error getting latest release, using stale: {}", e); + *latest = Some((now, previous.clone())); return Ok(previous.clone()); } diff --git a/cli/src/download_cache.rs b/cli/src/download_cache.rs index d3f05d2237f..cd02b02d75a 100644 --- a/cli/src/download_cache.rs +++ b/cli/src/download_cache.rs @@ -20,6 +20,7 @@ const KEEP_LRU: usize = 5; const STAGING_SUFFIX: &str = ".staging"; const RENAME_ATTEMPTS: u32 = 20; const RENAME_DELAY: std::time::Duration = std::time::Duration::from_millis(200); +const PERSISTED_STATE_FILE_NAME: &str = "lru.json"; #[derive(Clone)] pub struct DownloadCache { @@ -30,11 +31,16 @@ pub struct DownloadCache { impl DownloadCache { pub fn new(path: PathBuf) -> DownloadCache { DownloadCache { - state: PersistedState::new(path.join("lru.json")), + state: PersistedState::new(path.join(PERSISTED_STATE_FILE_NAME)), path, } } + /// Gets the value stored on the state + pub fn get(&self) -> Vec { + self.state.load() + } + /// Gets the download cache path. Names of cache entries can be formed by /// joining them to the path. pub fn path(&self) -> &Path { diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index 0579f8ef0dc..465f6e24230 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -674,7 +674,7 @@ where let write_line = |line: &str| -> std::io::Result<()> { if let Some(mut f) = log_file.as_ref() { f.write_all(line.as_bytes())?; - f.write_all(&[b'\n'])?; + f.write_all(b"\n")?; } if write_directly { println!("{}", line); diff --git a/package.json b/package.json index 4104623cf41..149a219512b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.94.0", - "distro": "36c6d77f96e54b1ad2233cd24fed8e8f08d5a388", + "distro": "fcaeb73de7ac6ff11a3e732c63987e01b88341e7", "author": { "name": "Microsoft Corporation" }, From 2e4bae90c63c2d990061d450f359ce5ab0ac3eb4 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 9 Sep 2024 08:53:30 -0700 Subject: [PATCH 07/45] cli: update rs to avoid arm64 bug --- build/azure-pipelines/cli/install-rust-posix.yml | 2 +- build/azure-pipelines/cli/install-rust-win32.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/cli/install-rust-posix.yml b/build/azure-pipelines/cli/install-rust-posix.yml index 89867143938..fee56e028f7 100644 --- a/build/azure-pipelines/cli/install-rust-posix.yml +++ b/build/azure-pipelines/cli/install-rust-posix.yml @@ -1,7 +1,7 @@ parameters: - name: channel type: string - default: 1.77 + default: 1.81 - name: targets default: [] type: object diff --git a/build/azure-pipelines/cli/install-rust-win32.yml b/build/azure-pipelines/cli/install-rust-win32.yml index 22fba8d7f6a..45a1cfd188e 100644 --- a/build/azure-pipelines/cli/install-rust-win32.yml +++ b/build/azure-pipelines/cli/install-rust-win32.yml @@ -1,7 +1,7 @@ parameters: - name: channel type: string - default: 1.77 + default: 1.81 - name: targets default: [] type: object From 1349397cf767bda1680b22c095cf18084e71cf43 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 9 Sep 2024 09:28:32 -0700 Subject: [PATCH 08/45] Remove vscode-nls-dev dependency (#228002) This was when extensions used the old localization stuff. None of this is used anymore. --- build/gulpfile.extensions.js | 5 - extensions/shared.webpack.config.js | 15 +- package-lock.json | 562 ---------------------------- package.json | 1 - 4 files changed, 2 insertions(+), 581 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index f00fa7a1ac3..4f745aebdc7 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -101,7 +101,6 @@ const tasks = compilations.map(function (tsconfigFile) { } function createPipeline(build, emitError, transpileOnly) { - const nlsDev = require('vscode-nls-dev'); const tsb = require('./lib/tsb'); const sourcemaps = require('gulp-sourcemaps'); @@ -126,7 +125,6 @@ const tasks = compilations.map(function (tsconfigFile) { .pipe(tsFilter) .pipe(util.loadSourcemaps()) .pipe(compilation()) - .pipe(build ? nlsDev.rewriteLocalizeCalls() : es.through()) .pipe(build ? util.stripSourceMappingURL() : es.through()) .pipe(sourcemaps.write('.', { sourceMappingURL: !build ? null : f => `${baseUrl}/${f.relative}.map`, @@ -136,9 +134,6 @@ const tasks = compilations.map(function (tsconfigFile) { sourceRoot: '../src/', })) .pipe(tsFilter.restore) - .pipe(build ? nlsDev.bundleMetaDataFiles(headerId, headerOut) : es.through()) - // Filter out *.nls.json file. We needed them only to bundle meta data file. - .pipe(filter(['**', '!**/*.nls.json'], { dot: true })) .pipe(reporter.end(emitError)); return es.duplex(input, output); diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index 4c258280812..6e5b9fd95ac 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -12,7 +12,6 @@ const path = require('path'); const fs = require('fs'); const merge = require('merge-options'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const { NLSBundlePlugin } = require('vscode-nls-dev/lib/webpack-bundler'); const { DefinePlugin, optimize } = require('webpack'); const tsLoaderOptions = { @@ -40,13 +39,6 @@ function withNodeDefaults(/**@type WebpackConfig & { context: string }*/extConfi test: /\.ts$/, exclude: /node_modules/, use: [{ - // vscode-nls-dev loader: - // * rewrite nls-calls - loader: 'vscode-nls-dev/lib/webpack-loader', - options: { - base: path.join(extConfig.context, 'src') - } - }, { // configure TypeScript loader: // * enable sources maps for end-to-end source maps loader: 'ts-loader', @@ -97,8 +89,7 @@ function nodePlugins(context) { patterns: [ { from: 'src', to: '.', globOptions: { ignore: ['**/test/**', '**/*.ts'] }, noErrorOnMissing: true } ] - }), - new NLSBundlePlugin(id) + }) ]; } /** @@ -196,9 +187,7 @@ function browserPlugins(context) { 'process.platform': JSON.stringify('web'), 'process.env': JSON.stringify({}), 'process.env.BROWSER_ENV': JSON.stringify('true') - }), - // TODO: bring this back once vscode-nls-dev supports browser - // new NLSBundlePlugin(id) + }) ]; } diff --git a/package-lock.json b/package-lock.json index 88d1a66925e..21f08c77576 100644 --- a/package-lock.json +++ b/package-lock.json @@ -156,7 +156,6 @@ "tslib": "^2.6.3", "typescript": "^5.7.0-dev.20240903", "util": "^0.12.4", - "vscode-nls-dev": "^3.3.1", "webpack": "^5.94.0", "webpack-cli": "^5.1.4", "webpack-stream": "^7.0.0", @@ -6699,119 +6698,6 @@ "node": ">=0.8.x" } }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/execa/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/execa/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa/node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/execa/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/execa/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/execa/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -11119,15 +11005,6 @@ "node": ">= 12" } }, - "node_modules/is": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", - "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU= sha512-NgGnzF+/wucMxle6i32obg/UjUqQruwlJUtjBpDEMXNq8vPLuaf28U4q+yes1I6J89FoErr7kDtBGICoNaY7rQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -12565,18 +12442,6 @@ "node": ">=0.10.0" } }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -12847,20 +12712,6 @@ "node": ">= 0.6" } }, - "node_modules/mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/memoizee": { "version": "0.4.15", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", @@ -13872,27 +13723,6 @@ "which": "bin/which" } }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/nth-check": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", @@ -14401,33 +14231,6 @@ "node": ">=8" } }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -17746,15 +17549,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -19258,362 +19052,6 @@ "node": ">= 0.10" } }, - "node_modules/vscode-nls-dev": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/vscode-nls-dev/-/vscode-nls-dev-3.3.1.tgz", - "integrity": "sha512-fug18D7CXb8pv8JoQ0D0JmZaIYDQoKLiyZxkAy5P8Cln/FwlNsdzwQILDph62EdGY5pvsJ2Jd1T5qgHAExe/tg==", - "dev": true, - "dependencies": { - "ansi-colors": "^3.2.3", - "clone": "^2.1.1", - "event-stream": "^3.3.4", - "fancy-log": "^1.3.3", - "glob": "^7.1.2", - "iconv-lite": "^0.4.19", - "is": "^3.2.1", - "source-map": "^0.6.1", - "typescript": "^2.6.2", - "vinyl": "^2.1.0", - "xml2js": "^0.4.19", - "yargs": "^13.2.4" - }, - "bin": { - "vscl": "lib/vscl.js" - } - }, - "node_modules/vscode-nls-dev/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/vscode-nls-dev/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/vscode-nls-dev/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/vscode-nls-dev/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/vscode-nls-dev/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vscode-nls-dev/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/vscode-nls-dev/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/vscode-nls-dev/node_modules/invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/vscode-nls-dev/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/vscode-nls-dev/node_modules/lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "dependencies": { - "invert-kv": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "dependencies": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vscode-nls-dev/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/vscode-nls-dev/node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vscode-nls-dev/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/vscode-nls-dev/node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "dev": true, - "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vscode-nls-dev/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/vscode-nls-dev/node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/vscode-nls-dev/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/vscode-nls-dev/node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "node_modules/vscode-nls-dev/node_modules/yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "dev": true, - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - } - }, - "node_modules/vscode-nls-dev/node_modules/yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, "node_modules/vscode-oniguruma": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", diff --git a/package.json b/package.json index 149a219512b..138ae5f7954 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,6 @@ "tslib": "^2.6.3", "typescript": "^5.7.0-dev.20240903", "util": "^0.12.4", - "vscode-nls-dev": "^3.3.1", "webpack": "^5.94.0", "webpack-cli": "^5.1.4", "webpack-stream": "^7.0.0", From 287757bd0a4ce8ed4669326e1257bb619dcc005b Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 9 Sep 2024 09:43:24 -0700 Subject: [PATCH 09/45] add status bar pinned action aria label (#227845) --- .../languageStatus/browser/languageStatus.contribution.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts index 36e4187a4b2..28a38f4a198 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts +++ b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts @@ -331,17 +331,19 @@ class LanguageStatus { // -- pin const actionBar = new ActionBar(right, { hoverDelegate: nativeHoverDelegate }); + const actionLabel: string = isPinned ? localize('unpin', "Remove from Status Bar") : localize('pin', "Add to Status Bar"); + actionBar.setAriaLabel(actionLabel); store.add(actionBar); let action: Action; if (!isPinned) { - action = new Action('pin', localize('pin', "Add to Status Bar"), ThemeIcon.asClassName(Codicon.pin), true, () => { + action = new Action('pin', actionLabel, ThemeIcon.asClassName(Codicon.pin), true, () => { this._dedicated.add(status.id); this._statusBarService.updateEntryVisibility(status.id, true); this._update(); this._storeState(); }); } else { - action = new Action('unpin', localize('unpin', "Remove from Status Bar"), ThemeIcon.asClassName(Codicon.pinned), true, () => { + action = new Action('unpin', actionLabel, ThemeIcon.asClassName(Codicon.pinned), true, () => { this._dedicated.delete(status.id); this._statusBarService.updateEntryVisibility(status.id, false); this._update(); From dc9412125d4e0a480593962ae2687e74e64af728 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:59:31 -0700 Subject: [PATCH 10/45] chore: bump micromatch (#228005) --- extensions/npm/package-lock.json | 20 +++++++++++--------- extensions/package-lock.json | 9 +++++---- package-lock.json | 9 +++++---- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/extensions/npm/package-lock.json b/extensions/npm/package-lock.json index 21a7c83df8e..4ee7ee7a0e3 100644 --- a/extensions/npm/package-lock.json +++ b/extensions/npm/package-lock.json @@ -193,15 +193,16 @@ } }, "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">=8.6" } }, "node_modules/minimatch": { @@ -252,9 +253,10 @@ } }, "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, diff --git a/extensions/package-lock.json b/extensions/package-lock.json index 63476fbb316..792ee4281c2 100644 --- a/extensions/package-lock.json +++ b/extensions/package-lock.json @@ -547,12 +547,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { diff --git a/package-lock.json b/package-lock.json index 21f08c77576..ea7a1817800 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12802,11 +12802,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { From 3a89990b2c4d0df8b98195b4e4d016596dbcb762 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 02:11:56 +0900 Subject: [PATCH 11/45] Bump yargs-parser and yargs (#227800) Bumps [yargs-parser](https://github.com/yargs/yargs-parser) and [yargs](https://github.com/yargs/yargs). These dependencies needed to be updated together. Updates `yargs-parser` from 13.1.1 to 21.1.1 - [Release notes](https://github.com/yargs/yargs-parser/releases) - [Changelog](https://github.com/yargs/yargs-parser/blob/main/CHANGELOG.md) - [Commits](https://github.com/yargs/yargs-parser/compare/v13.1.1...yargs-parser-v21.1.1) Updates `yargs` from 7.1.1 to 17.7.2 - [Release notes](https://github.com/yargs/yargs/releases) - [Changelog](https://github.com/yargs/yargs/blob/main/CHANGELOG.md) - [Commits](https://github.com/yargs/yargs/commits/v17.7.2) --- updated-dependencies: - dependency-name: yargs-parser dependency-type: indirect - dependency-name: yargs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index ea7a1817800..f4a1e6017c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8731,7 +8731,7 @@ "node_modules/gulp-cli/node_modules/camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo= sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8833,9 +8833,9 @@ "dev": true }, "node_modules/gulp-cli/node_modules/yargs": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz", - "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", "dev": true, "dependencies": { "camelcase": "^3.0.0", @@ -8850,13 +8850,13 @@ "string-width": "^1.0.2", "which-module": "^1.0.0", "y18n": "^3.2.1", - "yargs-parser": "5.0.0-security.0" + "yargs-parser": "^5.0.1" } }, "node_modules/gulp-cli/node_modules/yargs-parser": { - "version": "5.0.0-security.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz", - "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", "dev": true, "dependencies": { "camelcase": "^3.0.0", From dcc7af186a48d966210e12c41a0050deb1511ff0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 9 Sep 2024 19:18:38 +0200 Subject: [PATCH 12/45] Increase the max width of hover to a higher value (#227941) increasing the width to a higher value --- src/vs/editor/contrib/hover/browser/contentHoverWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts index 2ac48a73c46..5dc30190980 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts @@ -291,7 +291,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, 500, ContentHoverWidget._lastDimensions.width); + const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 750, ContentHoverWidget._lastDimensions.width); this._setHoverWidgetMaxDimensions(width, height); } From 6092f245833f7c7431825878819766716684a5b0 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 9 Sep 2024 11:39:52 -0700 Subject: [PATCH 13/45] WordHighlighter: fix queries against disposed models + blur logic (#227607) * wordHighlighter: fix queries against disposed models (use URIs) * add blur logic + clear old workerReq logic from when highlighting was only singleFile * imports * dispose models that we ref for the occurrence request * WHOOPS don't do that, it destroys every editor hah --- .../browser/wordHighlighter.ts | 162 +++++++++++------- .../browser/view/cellParts/codeCell.ts | 19 +- 2 files changed, 108 insertions(+), 73 deletions(-) diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index 656b2cf8ef9..4af05cb5aa3 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -10,6 +10,13 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { onUnexpectedError, onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../base/common/map.js'; +import { matchesScheme, Schemas } from '../../../../base/common/network.js'; +import { isEqual } from '../../../../base/common/resources.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IActiveCodeEditor, ICodeEditor, isDiffEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, registerModelAndPositionCommand } from '../../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; @@ -21,20 +28,15 @@ import { IWordAtPosition } from '../../../common/core/wordHelper.js'; import { CursorChangeReason, ICursorPositionChangedEvent } from '../../../common/cursorEvents.js'; import { IDiffEditor, IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; +import { registerEditorFeature } from '../../../common/editorFeatures.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; import { DocumentHighlight, DocumentHighlightProvider, MultiDocumentHighlightProvider } from '../../../common/languages.js'; +import { score } from '../../../common/languageSelector.js'; import { IModelDeltaDecoration, ITextModel, shouldSynchronizeModel } from '../../../common/model.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ITextModelService } from '../../../common/services/resolverService.js'; import { getHighlightDecorationOptions } from './highlightDecorations.js'; -import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { matchesScheme, Schemas } from '../../../../base/common/network.js'; -import { ResourceMap } from '../../../../base/common/map.js'; -import { score } from '../../../common/languageSelector.js'; -import { isEqual } from '../../../../base/common/resources.js'; import { TextualMultiDocumentHighlightFeature } from './textualHighlightProvider.js'; -import { registerEditorFeature } from '../../../common/editorFeatures.js'; const ctxHasWordHighlights = new RawContextKey('hasWordHighlights', false); @@ -58,7 +60,7 @@ export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry, model: ITextModel, position: Position, wordSeparators: string, token: CancellationToken, otherModels: ITextModel[]): Promise | null | undefined> { +export function getOccurrencesAcrossMultipleModels(registry: LanguageFeatureRegistry, model: ITextModel, position: Position, token: CancellationToken, otherModels: ITextModel[]): Promise | null | undefined> { const orderedByScore = registry.ordered(model); // in order of score ask the occurrences provider @@ -84,10 +86,9 @@ interface IOccurenceAtPositionRequest { interface IWordHighlighterQuery { modelInfo: { - model: ITextModel; + modelURI: URI; selection: Selection; } | null; - readonly word: IWordAtPosition | null; } abstract class OccurenceAtPositionRequest implements IOccurenceAtPositionRequest { @@ -175,7 +176,7 @@ class MultiModelOccurenceRequest extends OccurenceAtPositionRequest { } protected override _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise> { - return getOccurrencesAcrossMultipleModels(this._providers, model, selection.getPosition(), wordSeparators, token, this._otherModels).then(value => { + return getOccurrencesAcrossMultipleModels(this._providers, model, selection.getPosition(), token, this._otherModels).then(value => { if (!value) { return new ResourceMap(); } @@ -185,11 +186,11 @@ class MultiModelOccurenceRequest extends OccurenceAtPositionRequest { } -function computeOccurencesAtPosition(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string): IOccurenceAtPositionRequest { +function computeOccurencesAtPosition(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, wordSeparators: string): IOccurenceAtPositionRequest { return new SemanticOccurenceAtPositionRequest(model, selection, wordSeparators, registry); } -function computeOccurencesMultiModel(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string, otherModels: ITextModel[]): IOccurenceAtPositionRequest { +function computeOccurencesMultiModel(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, wordSeparators: string, otherModels: ITextModel[]): IOccurenceAtPositionRequest { return new MultiModelOccurenceRequest(model, selection, wordSeparators, registry, otherModels); } @@ -207,7 +208,10 @@ class WordHighlighter { private readonly model: ITextModel; private readonly decorations: IEditorDecorationsCollection; private readonly toUnhook = new DisposableStore(); + + private readonly textModelService: ITextModelService; private readonly codeEditorService: ICodeEditorService; + private occurrencesHighlight: string; private workerRequestTokenId: number = 0; @@ -221,20 +225,31 @@ class WordHighlighter { private readonly _hasWordHighlights: IContextKey; private _ignorePositionChangeEvent: boolean; - private readonly runDelayer: Delayer = this.toUnhook.add(new Delayer(50)); + private readonly runDelayer: Delayer = this.toUnhook.add(new Delayer(25)); private static storedDecorationIDs: ResourceMap = new ResourceMap(); private static query: IWordHighlighterQuery | null = null; - constructor(editor: IActiveCodeEditor, providers: LanguageFeatureRegistry, multiProviders: LanguageFeatureRegistry, contextKeyService: IContextKeyService, @ICodeEditorService codeEditorService: ICodeEditorService) { + constructor( + editor: IActiveCodeEditor, + providers: LanguageFeatureRegistry, + multiProviders: LanguageFeatureRegistry, + contextKeyService: IContextKeyService, + @ITextModelService textModelService: ITextModelService, + @ICodeEditorService codeEditorService: ICodeEditorService, + ) { this.editor = editor; this.providers = providers; this.multiDocumentProviders = multiProviders; + this.codeEditorService = codeEditorService; + this.textModelService = textModelService; + this._hasWordHighlights = ctxHasWordHighlights.bindTo(contextKeyService); this._ignorePositionChangeEvent = false; this.occurrencesHighlight = this.editor.getOption(EditorOption.occurrencesHighlight); this.model = this.editor.getModel(); + this.toUnhook.add(editor.onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { if (this._ignorePositionChangeEvent) { // We are changing the position => ignore this event @@ -267,10 +282,8 @@ class WordHighlighter { this.toUnhook.add(editor.onDidChangeModel((e) => { if (!e.newModelUrl && e.oldModelUrl) { this._stopSingular(); - } else { - if (WordHighlighter.query) { - this._run(); - } + } else if (WordHighlighter.query) { + this._run(); } })); this.toUnhook.add(editor.onDidChangeConfiguration((e) => { @@ -282,7 +295,7 @@ class WordHighlighter { this._stopAll(); break; case 'singleFile': - this._stopAll(WordHighlighter.query?.modelInfo?.model); + this._stopAll(WordHighlighter.query?.modelInfo?.modelURI); break; case 'multiFile': if (WordHighlighter.query) { @@ -295,6 +308,19 @@ class WordHighlighter { } } })); + this.toUnhook.add(editor.onDidBlurEditorWidget(() => { + // logic is as follows + // - didBlur => active null => stopall + // - didBlur => active nb => if this.editor is notebook, do nothing (new cell, so we don't want to stopAll) + // active nb => if this.editor is NOT nb, stopAll + + const activeEditor = this.codeEditorService.getFocusedCodeEditor(); + if (!activeEditor) { // clicked into nb cell list, outline, terminal, etc + this._stopAll(); + } else if (activeEditor.getModel()?.uri.scheme === Schemas.vscodeNotebookCell && this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell) { // switched tabs from non-nb to nb + this._stopAll(); + } + })); this.decorations = this.editor.createDecorationsCollection(); this.workerRequestTokenId = 0; @@ -396,12 +422,12 @@ class WordHighlighter { } } - private _removeAllDecorations(preservedModel?: ITextModel): void { + private _removeAllDecorations(preservedModel?: URI): void { const currentEditors = this.codeEditorService.listCodeEditors(); const deleteURI = []; // iterate over editors and store models in currentModels for (const editor of currentEditors) { - if (!editor.hasModel() || isEqual(editor.getModel().uri, preservedModel?.uri)) { + if (!editor.hasModel() || isEqual(editor.getModel().uri, preservedModel)) { continue; } @@ -435,7 +461,7 @@ class WordHighlighter { this._removeSingleDecorations(); if (this.editor.hasTextFocus()) { - if (this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell && WordHighlighter.query?.modelInfo?.model.uri.scheme !== Schemas.vscodeNotebookCell) { // clear query if focused non-nb editor + if (this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell && WordHighlighter.query?.modelInfo?.modelURI.scheme !== Schemas.vscodeNotebookCell) { // clear query if focused non-nb editor WordHighlighter.query = null; this._run(); // TODO: @Yoyokrazy -- investigate why we need a full rerun here. likely addressed a case/patch in the first iteration of this feature } else { // remove modelInfo to account for nb cell being disposed @@ -464,7 +490,7 @@ class WordHighlighter { } } - private _stopAll(preservedModel?: ITextModel): void { + private _stopAll(preservedModel?: URI): void { // Remove any existing decorations // TODO: @Yoyokrazy -- this triggers as notebooks scroll, causing highlights to disappear momentarily. // maybe a nb type check? @@ -582,9 +608,8 @@ class WordHighlighter { return currentModels; } - private _run(multiFileConfigChange?: boolean): void { + private async _run(multiFileConfigChange?: boolean): Promise { - let workerRequestIsValid; const hasTextFocus = this.editor.hasTextFocus(); if (!hasTextFocus) { // new nb cell scrolled in, didChangeModel fires @@ -615,41 +640,18 @@ class WordHighlighter { return; } - // All the effort below is trying to achieve this: - // - when cursor is moved to a word, trigger immediately a findOccurrences request - // - 250ms later after the last cursor move event, render the occurrences - // - no flickering! - workerRequestIsValid = (this.workerRequest && this.workerRequest.isValid(this.model, editorSelection, this.decorations)); - WordHighlighter.query = { modelInfo: { - model: this.model, + modelURI: this.model.uri, selection: editorSelection, - }, - word: word + } }; } - // There are 4 cases: - // a) old workerRequest is valid & completed, renderDecorationsTimer fired - // b) old workerRequest is valid & completed, renderDecorationsTimer not fired - // c) old workerRequest is valid, but not completed - // d) old workerRequest is not valid - - // For a) no action is needed - // For c), member 'lastCursorPositionChangeTime' will be used when installing the timer so no action is needed this.lastCursorPositionChangeTime = (new Date()).getTime(); - if (workerRequestIsValid) { - if (this.workerRequestCompleted && this.renderDecorationsTimer !== -1) { - // case b) - // Delay the firing of renderDecorationsTimer by an extra 250 ms - clearTimeout(this.renderDecorationsTimer); - this.renderDecorationsTimer = -1; - this._beginRenderDecorations(); - } - } else if (isEqual(this.editor.getModel().uri, WordHighlighter.query.modelInfo?.model.uri)) { // only trigger new worker requests from the primary model that initiated the query + if (isEqual(this.editor.getModel().uri, WordHighlighter.query.modelInfo?.modelURI)) { // only trigger new worker requests from the primary model that initiated the query // case d) // check if the new queried word is contained in the range of a stored decoration for this model @@ -664,7 +666,7 @@ class WordHighlighter { // stop all previous actions if new word is highlighted // if we trigger the run off a setting change -> multifile highlighting, we do not want to remove decorations from this model - this._stopAll(multiFileConfigChange ? this.model : undefined); + this._stopAll(multiFileConfigChange ? this.model.uri : undefined); const myRequestId = ++this.workerRequestTokenId; this.workerRequestCompleted = false; @@ -675,10 +677,35 @@ class WordHighlighter { // 1) we have text focus, and a valid query was updated. // 2) we do not have text focus, and a valid query is cached. // the query will ALWAYS have the correct data for the current highlight request, so it can always be passed to the workerRequest safely - if (!WordHighlighter.query || !WordHighlighter.query.modelInfo || WordHighlighter.query.modelInfo.model.isDisposed()) { + if (!WordHighlighter.query || !WordHighlighter.query.modelInfo) { return; } - this.workerRequest = this.computeWithModel(WordHighlighter.query.modelInfo.model, WordHighlighter.query.modelInfo.selection, WordHighlighter.query.word, otherModelsToHighlight); + const queryModelRef = await this.textModelService.createModelReference(WordHighlighter.query.modelInfo.modelURI); + const queryModel = queryModelRef.object.textEditorModel; + this.workerRequest = this.computeWithModel(queryModel, WordHighlighter.query.modelInfo.selection, otherModelsToHighlight); + + this.workerRequest?.result.then(data => { + if (myRequestId === this.workerRequestTokenId) { + this.workerRequestCompleted = true; + this.workerRequestValue = data || []; + this._beginRenderDecorations(); + } + }, onUnexpectedError); + } else if (this.model.uri.scheme === Schemas.vscodeNotebookCell) { + // new wordHighlighter coming from a different model, NOT the query model, need to create a textModel ref + + // this._stopAll(multiFileConfigChange ? this.model.uri : undefined); + + const myRequestId = ++this.workerRequestTokenId; + this.workerRequestCompleted = false; + + if (!WordHighlighter.query || !WordHighlighter.query.modelInfo) { + return; + } + + const queryModelRef = await this.textModelService.createModelReference(WordHighlighter.query.modelInfo.modelURI); + const queryModel = queryModelRef.object.textEditorModel; + this.workerRequest = this.computeWithModel(queryModel, WordHighlighter.query.modelInfo.selection, [this.model]); this.workerRequest?.result.then(data => { if (myRequestId === this.workerRequestTokenId) { @@ -690,11 +717,11 @@ class WordHighlighter { } } - private computeWithModel(model: ITextModel, selection: Selection, word: IWordAtPosition | null, otherModels: ITextModel[]): IOccurenceAtPositionRequest | null { + private computeWithModel(model: ITextModel, selection: Selection, otherModels: ITextModel[]): IOccurenceAtPositionRequest | null { if (!otherModels.length) { - return computeOccurencesAtPosition(this.providers, model, selection, word, this.editor.getOption(EditorOption.wordSeparators)); + return computeOccurencesAtPosition(this.providers, model, selection, this.editor.getOption(EditorOption.wordSeparators)); } else { - return computeOccurencesMultiModel(this.multiDocumentProviders, model, selection, word, this.editor.getOption(EditorOption.wordSeparators), otherModels); + return computeOccurencesMultiModel(this.multiDocumentProviders, model, selection, this.editor.getOption(EditorOption.wordSeparators), otherModels); } } @@ -755,6 +782,9 @@ class WordHighlighter { } } } + + // clear the worker request when decorations are completed + this.workerRequest = null; } public dispose(): void { @@ -773,16 +803,26 @@ export class WordHighlighterContribution extends Disposable implements IEditorCo private _wordHighlighter: WordHighlighter | null; - constructor(editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, @ICodeEditorService codeEditorService: ICodeEditorService) { + constructor( + editor: ICodeEditor, + @IContextKeyService contextKeyService: IContextKeyService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ITextModelService textModelService: ITextModelService, + ) { super(); this._wordHighlighter = null; const createWordHighlighterIfPossible = () => { if (editor.hasModel() && !editor.getModel().isTooLargeForTokenization()) { - this._wordHighlighter = new WordHighlighter(editor, languageFeaturesService.documentHighlightProvider, languageFeaturesService.multiDocumentHighlightProvider, contextKeyService, codeEditorService); + this._wordHighlighter = new WordHighlighter(editor, languageFeaturesService.documentHighlightProvider, languageFeaturesService.multiDocumentHighlightProvider, contextKeyService, textModelService, codeEditorService); } }; this._register(editor.onDidChangeModel((e) => { if (this._wordHighlighter) { + if (!e.newModelUrl && e.oldModelUrl?.scheme !== Schemas.vscodeNotebookCell) { // happens when switching tabs to a notebook that has focus in the cell list, no new model URI (this also doesn't make it to the wordHighlighter, bc no editor.hasModel) + this.wordHighlighter?.stop(); + } + this._wordHighlighter.dispose(); this._wordHighlighter = null; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index f44b10e4e84..8cdb4f05629 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -3,36 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from '../../../../../../nls.js'; import * as DOM from '../../../../../../base/browser/dom.js'; import { raceCancellation } from '../../../../../../base/common/async.js'; import { CancellationTokenSource } from '../../../../../../base/common/cancellation.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; -import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { Event } from '../../../../../../base/common/event.js'; import { Disposable, IDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; import { clamp } from '../../../../../../base/common/numbers.js'; import * as strings from '../../../../../../base/common/strings.js'; +import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; import { IDimension } from '../../../../../../editor/common/core/dimension.js'; import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; import { tokenizeToStringSync } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; import { IReadonlyTextBuffer, ITextModel } from '../../../../../../editor/common/model.js'; -import { localize } from '../../../../../../nls.js'; +import { CodeActionController } from '../../../../../../editor/contrib/codeAction/browser/codeActionController.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../../../platform/keybinding/common/keybinding.js'; import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; +import { INotebookExecutionStateService } from '../../../common/notebookExecutionStateService.js'; import { CellFocusMode, EXPAND_CELL_INPUT_COMMAND_ID, IActiveNotebookEditorDelegate } from '../../notebookBrowser.js'; +import { CodeCellViewModel, outputDisplayLimit } from '../../viewModel/codeCellViewModel.js'; import { CellPartsCollection } from '../cellPart.js'; +import { NotebookCellEditorPool } from '../notebookCellEditorPool.js'; +import { CodeCellRenderTemplate } from '../notebookRenderingCommon.js'; import { CellEditorOptions } from './cellEditorOptions.js'; import { CellOutputContainer } from './cellOutput.js'; import { CollapsedCodeCellExecutionIcon } from './codeCellExecutionIcon.js'; -import { CodeCellRenderTemplate } from '../notebookRenderingCommon.js'; -import { CodeCellViewModel, outputDisplayLimit } from '../../viewModel/codeCellViewModel.js'; -import { INotebookExecutionStateService } from '../../../common/notebookExecutionStateService.js'; -import { WordHighlighterContribution } from '../../../../../../editor/contrib/wordHighlighter/browser/wordHighlighter.js'; -import { CodeActionController } from '../../../../../../editor/contrib/codeAction/browser/codeActionController.js'; -import { NotebookCellEditorPool } from '../notebookCellEditorPool.js'; export class CodeCell extends Disposable { private _outputContainerRenderer: CellOutputContainer; @@ -354,13 +353,9 @@ export class CodeCell extends Disposable { })); this._register(this.templateData.editor.onDidBlurEditorWidget(() => { - WordHighlighterContribution.get(this.templateData.editor)?.stopHighlighting(); CodeActionController.get(this.templateData.editor)?.hideCodeActions(); CodeActionController.get(this.templateData.editor)?.hideLightBulbWidget(); })); - this._register(this.templateData.editor.onDidFocusEditorWidget(() => { - WordHighlighterContribution.get(this.templateData.editor)?.restoreViewState(true); - })); } private _reigsterModelListeners(model: ITextModel) { From 1ed956dfa9bfc7bd73a97f104c05aec4c5e26383 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Sep 2024 13:45:09 -0700 Subject: [PATCH 14/45] Remove `SymbolInformation` change --- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 +- src/vs/workbench/api/common/extHostTypes.ts | 4 ++-- .../contrib/chat/browser/media/chatInlineAnchorWidget.css | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index c0652cccd45..1abbe45add0 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2505,7 +2505,7 @@ export namespace ChatResponseAnchorPart { URI.isUri(value.inlineReference) ? value.inlineReference : 'location' in value.inlineReference - ? WorkspaceSymbol.to(value.inlineReference) + ? WorkspaceSymbol.to(value.inlineReference) as vscode.SymbolInformation : Location.to(value.inlineReference), part.name ); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 1811501aef7..58e0404d8ce 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1346,14 +1346,14 @@ export class SymbolInformation { location!: Location; kind: SymbolKind; tags?: SymbolTag[]; - containerName: string; + containerName: string | undefined; constructor(name: string, kind: SymbolKind, containerName: string | undefined, location: Location); constructor(name: string, kind: SymbolKind, range: Range, uri?: URI, containerName?: string); constructor(name: string, kind: SymbolKind, rangeOrContainer: string | undefined | Range, locationOrUri?: Location | URI, containerName?: string) { this.name = name; this.kind = kind; - this.containerName = containerName ?? ''; + this.containerName = containerName; if (typeof rangeOrContainer === 'string') { this.containerName = rangeOrContainer; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css index cd4255fcefe..11900cf4ca1 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css @@ -9,8 +9,7 @@ } .chat-inline-anchor-widget { - outline: 1px solid var(--vscode-chat-requestBorder, var(--vscode-input-background, transparent)); - outline-offset: -1px; + border: 1px solid var(--vscode-chat-requestBorder, var(--vscode-input-background, transparent)); border-radius: 4px; } From 378fb48f25244e29719af86b50647c204bf121ad Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 9 Sep 2024 13:53:58 -0700 Subject: [PATCH 15/45] Tweak css --- .../contrib/chat/browser/chatInlineAnchorWidget.ts | 5 ++++- .../contrib/chat/browser/media/chatInlineAnchorWidget.css | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts index 4dfab620aff..6f3660886e7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts @@ -57,9 +57,11 @@ export class InlineAnchorWidget extends Disposable { let location: { readonly uri: URI; readonly range?: IRange }; let contextMenuId: MenuId; + let contextMenuArg: URI | { readonly uri: URI; readonly range?: IRange }; if (data.kind === 'symbol') { location = data.symbol.location; contextMenuId = MenuId.ChatInlineSymbolAnchorContext; + contextMenuArg = location; const icon = SymbolKinds.toIcon(data.symbol.kind); resourceLabel.setLabel(`$(${icon.id}) ${data.symbol.name}`, undefined, {}); @@ -83,6 +85,7 @@ export class InlineAnchorWidget extends Disposable { } else { location = data; contextMenuId = MenuId.ChatInlineResourceAnchorContext; + contextMenuArg = location.uri; const label = labelService.getUriBasenameLabel(location.uri); const title = location.range && data.kind !== 'symbol' ? @@ -106,7 +109,7 @@ export class InlineAnchorWidget extends Disposable { contextKeyService, getAnchor: () => event, getActions: () => { - const menu = menuService.getMenuActions(contextMenuId, contextKeyService, { arg: location }); + const menu = menuService.getMenuActions(contextMenuId, contextKeyService, { arg: contextMenuArg }); const primary: IAction[] = []; createAndFillInContextMenuActions(menu, primary); return primary; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css index 11900cf4ca1..22bc19f8bd0 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css @@ -13,6 +13,10 @@ border-radius: 4px; } +.chat-inline-anchor-widget:hover { + background-color: var(--vscode-list-hoverBackground); +} + .chat-inline-anchor-widget .monaco-icon-label { display: inline-flex; margin: 0 1px; @@ -31,10 +35,6 @@ color: reset-layer !important; } -.chat-inline-anchor-widget:hover .monaco-icon-label { - background-color: var(--vscode-list-hoverBackground); -} - .chat-inline-anchor-widget .monaco-icon-label::before { height: auto; padding-right: 3px; From 76fb6affe4efcb8638228defecc6ea590383b49d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 9 Sep 2024 23:32:42 +0200 Subject: [PATCH 16/45] =?UTF-8?q?SCM=20Graph=20-=20=F0=9F=92=84=20more=20o?= =?UTF-8?q?bservable=20clean-up=20(#228021)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SCM Graph - clean-up graph rendering * Fix regression with repository picker --- .../base/common/observableInternal/utils.ts | 9 ++ .../contrib/scm/browser/scmHistoryViewPane.ts | 137 ++++++++---------- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/src/vs/base/common/observableInternal/utils.ts b/src/vs/base/common/observableInternal/utils.ts index b4230118e88..3ce3805792e 100644 --- a/src/vs/base/common/observableInternal/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -301,6 +301,15 @@ class ObservableSignal extends BaseObservable implements } } +export function signalFromObservable(owner: DebugOwner | undefined, observable: IObservable): IObservable { + return derivedOpts({ + owner, + equalsFn: () => false, + }, reader => { + observable.read(reader); + }); +} + /** * @deprecated Use `debouncedObservable2` instead. */ diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index ea5a76d79f6..828e8d60f33 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -16,7 +16,7 @@ import { fromNow } from '../../../../base/common/date.js'; import { createMatches, FuzzyScore, IMatch } from '../../../../base/common/filters.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; -import { autorun, autorunWithStoreHandleChanges, derived, derivedOpts, IObservable, observableValue, waitForState } from '../../../../base/common/observable.js'; +import { autorun, autorunWithStore, autorunWithStoreHandleChanges, derived, IObservable, observableValue, waitForState } from '../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -48,7 +48,7 @@ import { ActionRunner, IAction, IActionRunner } from '../../../../base/common/ac import { delta, groupBy, tail } from '../../../../base/common/arrays.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { IProgressService } from '../../../../platform/progress/common/progress.js'; -import { constObservable, latestChangedValue, observableFromEvent, runOnChange } from '../../../../base/common/observableInternal/utils.js'; +import { constObservable, latestChangedValue, observableFromEvent, runOnChange, signalFromObservable } from '../../../../base/common/observableInternal/utils.js'; import { ContextKeys } from './scmViewPane.js'; import { IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IDropdownMenuActionViewItemOptions } from '../../../../base/browser/ui/dropdown/dropdownActionViewItem.js'; @@ -59,7 +59,6 @@ import { Event } from '../../../../base/common/event.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { clamp } from '../../../../base/common/numbers.js'; import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; -import { structuralEquals } from '../../../../base/common/equals.js'; import { compare } from '../../../../base/common/strings.js'; type TreeElement = SCMHistoryItemViewModelTreeElement | SCMHistoryItemLoadMoreTreeElement; @@ -823,8 +822,8 @@ class RepositoryPicker extends Disposable { }; constructor( - private readonly _scmViewService: ISCMViewService, - private readonly _quickInputService: IQuickInputService, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @ISCMViewService private readonly _scmViewService: ISCMViewService ) { super(); } @@ -1063,93 +1062,83 @@ export class SCMHistoryViewPane extends ViewPane { // Wait for first repository to be initialized await waitForState(firstRepositoryInitialized); + // Set tree input this._treeOperationSequencer.queue(async () => { await this._tree.setInput(this._treeViewModel); this._tree.scrollTop = 0; }); // Repository change - this._visibilityDisposables.add( - autorunWithStoreHandleChanges<{ refresh: boolean }>({ - owner: this, - createEmptyChangeSummary: () => ({ refresh: false }), - handleChange(_, changeSummary) { - changeSummary.refresh = true; - return true; - }, - }, (reader, changeSummary, store) => { - const repository = this._treeViewModel.repository.read(reader); - const historyProvider = repository?.provider.historyProvider.read(reader); - if (!repository || !historyProvider) { - return; - } + let isFirstRun = true; + this._visibilityDisposables.add(autorunWithStore((reader, store) => { + const repository = this._treeViewModel.repository.read(reader); + const historyProvider = repository?.provider.historyProvider.read(reader); + if (!repository || !historyProvider) { + return; + } - // Update context - this._scmProviderCtx.set(repository.provider.contextValue); + // Update context + this._scmProviderCtx.set(repository.provider.contextValue); - // Checkout, Commit, and Publish - const historyItemGroup = derivedOpts<{ id: string; revision?: string; remoteId?: string } | undefined>({ + // Commit, Checkout + const historyItemRefSignal = signalFromObservable(this, historyProvider.currentHistoryItemRef); + + // Publish + const historyItemRefRemoteIdSignal = signalFromObservable(this, derived(reader => { + return historyProvider.currentHistoryItemRemoteRef.read(reader)?.id; + })); + + // Fetch, Push + const historyItemRefRemoteRevisionSignal = signalFromObservable(this, derived(reader => { + return historyProvider.currentHistoryItemRemoteRef.read(reader)?.revision; + })); + + // HistoryItemRefs changed + store.add( + autorunWithStoreHandleChanges<{ refresh: boolean | 'ifScrollTop' }>({ owner: this, - equalsFn: structuralEquals - }, reader => { - const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup.read(reader); - return currentHistoryItemGroup ? { - id: currentHistoryItemGroup.id, - revision: currentHistoryItemGroup.revision, - remoteId: currentHistoryItemGroup.remote?.id - } : undefined; - }); + createEmptyChangeSummary: () => ({ refresh: false }), + handleChange(context, changeSummary) { + changeSummary.refresh = context.didChange(historyItemRefRemoteRevisionSignal) ? 'ifScrollTop' : true; + return true; + }, + }, (reader, changeSummary) => { + historyItemRefSignal.read(reader); + historyItemRefRemoteIdSignal.read(reader); + historyItemRefRemoteRevisionSignal.read(reader); - // Fetch, Push - const historyItemRemoteRevision = derived(reader => { - return historyProvider.currentHistoryItemGroup.read(reader)?.remote?.revision; - }); + if (changeSummary.refresh === true) { + this.refresh(); + return; + } - // HistoryItemGroup change - store.add( - autorunWithStoreHandleChanges<{ refresh: boolean | 'ifScrollTop' }>({ - owner: this, - createEmptyChangeSummary: () => ({ refresh: false }), - handleChange(context, changeSummary) { - changeSummary.refresh = context.didChange(historyItemRemoteRevision) ? 'ifScrollTop' : true; - return true; - }, - }, (reader, changeSummary) => { - const historyItemGroupValue = historyItemGroup.read(reader); - const historyItemRemoteRevisionValue = historyItemRemoteRevision.read(reader); - - if ((!historyItemGroupValue && !historyItemRemoteRevisionValue) || changeSummary.refresh === false) { - return; - } - - if (changeSummary.refresh === true) { + if (changeSummary.refresh === 'ifScrollTop') { + // Remote revision changes can occur as a result of a user action (Fetch, Push) but + // it can also occur as a result of background action (Auto Fetch). If the tree is + // scrolled to the top, we can safely refresh the tree. + if (this._tree.scrollTop === 0) { this.refresh(); return; } - if (changeSummary.refresh === 'ifScrollTop') { - // Remote revision changes can occur as a result of a user action (Fetch, Push) but - // it can also occur as a result of background action (Auto Fetch). If the tree is - // scrolled to the top, we can safely refresh the tree. - if (this._tree.scrollTop === 0) { - this.refresh(); - return; - } - - // Show the "Outdated" badge on the view - this._repositoryOutdated.set(true, undefined); - } - })); - - // HistoryItemRefs filter changed - store.add(runOnChange(this._treeViewModel.historyItemsFilter, () => { - this.refresh(); + // Show the "Outdated" badge on the view + this._repositoryOutdated.set(true, undefined); + } })); - if (changeSummary.refresh) { - this.refresh(); - } + // HistoryItemRefs filter changed + store.add(runOnChange(this._treeViewModel.historyItemsFilter, () => { + this.refresh(); })); + + // We skip refreshing the graph on the first execution of the autorun + // since the graph for the first repository is rendered when the tree + // input is set. + if (!isFirstRun) { + this.refresh(); + } + isFirstRun = false; + })); } else { this._visibilityDisposables.clear(); } From dd9e7ee3443317022e62d20623d46c9b1ecf81b3 Mon Sep 17 00:00:00 2001 From: John Murray Date: Mon, 9 Sep 2024 22:33:55 +0100 Subject: [PATCH 17/45] Add actions to preview editor tab's hover (#226023) * WIP: show hover with actions on preview editor tab * Refactor to support `"workbench.editor.showTabs": "single"` * Manage hover delegates by index to handle closing correctly * Add some dispose calls * Experiment with a quieter hover * Fix problems caused by commented-out code * Safer use of editor title in hover markdown * Subtler link to preview mode doc * Use gear icon and go oto settings editor * Make clickable hover easier to mouse onto * Remove linkHandler experiment * Use simplified hover on single-tab mode too * Simplify now that we aren't adding statusbar commands to hovers * Reinstate lost blank line --------- Co-authored-by: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> --- .../browser/parts/editor/editorTabsControl.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index 477f0115bf1..d03297e004f 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -47,6 +47,8 @@ import { ServiceCollection } from '../../../../platform/instantiation/common/ser import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; export class EditorCommandsContextActionRunner extends ActionRunner { @@ -452,8 +454,17 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC return this.groupsView.partOptions.tabHeight !== 'compact' ? EditorTabsControl.EDITOR_TAB_HEIGHT.normal : EditorTabsControl.EDITOR_TAB_HEIGHT.compact; } - protected getHoverTitle(editor: EditorInput): string { - return editor.getTitle(Verbosity.LONG); + protected getHoverTitle(editor: EditorInput): string | IManagedHoverTooltipMarkdownString { + const title = editor.getTitle(Verbosity.LONG); + if (!this.tabsModel.isPinned(editor)) { + return { + markdown: new MarkdownString('', { supportThemeIcons: true, isTrusted: true }). + appendText(title). + appendMarkdown(' (_preview_ [$(gear)](command:workbench.action.openSettings?%5B%22workbench.editor.enablePreview%22%5D "Configure Preview Mode"))'), + markdownNotSupportedFallback: title + ' (preview)' + }; + } + return title; } protected getHoverDelegate(): IHoverDelegate { From d8af24e15daeb1ad5bd0ee4858852e60ea545c66 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 01:51:31 +0200 Subject: [PATCH 18/45] SCM - wire-up the onDidChangeHistoryItemRefs event (#228042) Wire-up the event --- extensions/git/src/historyProvider.ts | 85 ++++++++++++------- extensions/git/src/util.ts | 57 ++++++++++++- src/vs/workbench/api/browser/mainThreadSCM.ts | 37 +++++++- .../workbench/api/common/extHost.protocol.ts | 7 ++ src/vs/workbench/api/common/extHostSCM.ts | 11 +++ .../workbench/contrib/scm/common/history.ts | 8 ++ .../vscode.proposed.scmHistoryProvider.d.ts | 10 +-- 7 files changed, 177 insertions(+), 38 deletions(-) diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index e9aefa63dd1..872582dae6e 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -4,21 +4,53 @@ *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryItemGroup, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryChangeEvent, SourceControlHistoryItemRef, l10n } from 'vscode'; +import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryItemGroup, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryItemRef, l10n, SourceControlHistoryItemRefsChangeEvent } from 'vscode'; import { Repository, Resource } from './repository'; -import { IDisposable, dispose } from './util'; +import { IDisposable, deltaHistoryItemRefs, dispose } from './util'; import { toGitUri } from './uri'; -import { Branch, LogOptions, RefType } from './api/git'; +import { Branch, LogOptions, Ref, RefType } from './api/git'; import { emojify, ensureEmojis } from './emoji'; import { Commit } from './git'; +function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { + switch (ref.type) { + case RefType.RemoteHead: + return { + id: `refs/remotes/${ref.remote}/${ref.name}`, + name: ref.name ?? '', + description: ref.commit ? l10n.t('Remote branch at {0}', ref.commit.substring(0, 8)) : undefined, + revision: ref.commit, + icon: new ThemeIcon('cloud'), + category: l10n.t('remote branches') + }; + case RefType.Tag: + return { + id: `refs/tags/${ref.name}`, + name: ref.name ?? '', + description: ref.commit ? l10n.t('Tag at {0}', ref.commit.substring(0, 8)) : undefined, + revision: ref.commit, + icon: new ThemeIcon('tag'), + category: l10n.t('tags') + }; + default: + return { + id: `refs/heads/${ref.name}`, + name: ref.name ?? '', + description: ref.commit ? ref.commit.substring(0, 8) : undefined, + revision: ref.commit, + icon: new ThemeIcon('git-branch'), + category: l10n.t('branches') + }; + } +} + export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable { private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter(); readonly onDidChangeCurrentHistoryItemGroup: Event = this._onDidChangeCurrentHistoryItemGroup.event; - private readonly _onDidChangeHistory = new EventEmitter(); - readonly onDidChangeHistory: Event = this._onDidChangeHistory.event; + private readonly _onDidChangeHistoryItemRefs = new EventEmitter(); + readonly onDidChangeHistoryItemRefs: Event = this._onDidChangeHistoryItemRefs.event; private readonly _onDidChangeDecorations = new EventEmitter(); readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; @@ -30,6 +62,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec this._onDidChangeCurrentHistoryItemGroup.fire(); } + private historyItemRefs: SourceControlHistoryItemRef[] = []; private historyItemDecorations = new Map(); private disposables: Disposable[] = []; @@ -80,6 +113,21 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec }; this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] currentHistoryItemGroup: ${JSON.stringify(this.currentHistoryItemGroup)}`); + + // Refs (alphabetically) + const refs = await this.repository.getRefs({ sort: 'alphabetically' }); + const historyItemRefs = refs.map(ref => toSourceControlHistoryItemRef(ref)); + + const delta = deltaHistoryItemRefs(this.historyItemRefs, historyItemRefs); + this._onDidChangeHistoryItemRefs.fire(delta); + this.historyItemRefs = historyItemRefs; + + const deltaLog = { + added: delta.added.map(ref => ref.id), + modified: delta.modified.map(ref => ref.id), + removed: delta.removed.map(ref => ref.id) + }; + this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] historyItemRefs: ${JSON.stringify(deltaLog)}`); } async provideHistoryItemRefs(): Promise { @@ -92,34 +140,13 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec for (const ref of refs) { switch (ref.type) { case RefType.RemoteHead: - remoteBranches.push({ - id: `refs/remotes/${ref.remote}/${ref.name}`, - name: ref.name ?? '', - description: ref.commit ? l10n.t('Remote branch at {0}', ref.commit.substring(0, 8)) : undefined, - revision: ref.commit, - icon: new ThemeIcon('cloud'), - category: l10n.t('remote branches') - }); + remoteBranches.push(toSourceControlHistoryItemRef(ref)); break; case RefType.Tag: - tags.push({ - id: `refs/tags/${ref.name}`, - name: ref.name ?? '', - description: ref.commit ? l10n.t('Tag at {0}', ref.commit.substring(0, 8)) : undefined, - revision: ref.commit, - icon: new ThemeIcon('tag'), - category: l10n.t('tags') - }); + tags.push(toSourceControlHistoryItemRef(ref)); break; default: - branches.push({ - id: `refs/heads/${ref.name}`, - name: ref.name ?? '', - description: ref.commit ? ref.commit.substring(0, 8) : undefined, - revision: ref.commit, - icon: new ThemeIcon('git-branch'), - category: l10n.t('branches') - }); + branches.push(toSourceControlHistoryItemRef(ref)); break; } } diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index eac6f0384b9..bf33411c3b3 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Disposable, EventEmitter } from 'vscode'; +import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef } from 'vscode'; import { dirname, sep, relative } from 'path'; import { Readable } from 'stream'; import { promises as fs, createReadStream } from 'fs'; @@ -513,3 +513,58 @@ export namespace Versions { return from(major, minor, patch, pre); } } + +export function deltaHistoryItemRefs(before: SourceControlHistoryItemRef[], after: SourceControlHistoryItemRef[]): { + added: SourceControlHistoryItemRef[]; + modified: SourceControlHistoryItemRef[]; + removed: SourceControlHistoryItemRef[]; +} { + if (before.length === 0) { + return { added: after, modified: [], removed: [] }; + } + + const added: SourceControlHistoryItemRef[] = []; + const modified: SourceControlHistoryItemRef[] = []; + const removed: SourceControlHistoryItemRef[] = []; + + let beforeIdx = 0; + let afterIdx = 0; + + while (true) { + if (beforeIdx === before.length) { + added.push(...after.slice(afterIdx)); + break; + } + if (afterIdx === after.length) { + removed.push(...before.slice(beforeIdx)); + break; + } + + const beforeElement = before[beforeIdx]; + const afterElement = after[afterIdx]; + + const result = beforeElement.id.localeCompare(afterElement.id); + + if (result === 0) { + if (beforeElement.revision !== afterElement.revision) { + // modified + modified.push(afterElement); + } + + beforeIdx += 1; + afterIdx += 1; + } else if (result < 0) { + // beforeElement is smaller -> before element removed + removed.push(beforeElement); + + beforeIdx += 1; + } else if (result > 0) { + // beforeElement is greater -> after element added + added.push(afterElement); + + afterIdx += 1; + } + } + + return { added, modified, removed }; +} diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 7ab6d69e6ae..d713066de80 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -6,10 +6,10 @@ import { Barrier } from '../../../base/common/async.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { Event, Emitter } from '../../../base/common/event.js'; -import { derivedOpts, observableValue, observableValueOpts } from '../../../base/common/observable.js'; +import { derivedOpts, IObservable, observableValue, observableValueOpts } from '../../../base/common/observable.js'; import { IDisposable, DisposableStore, combinedDisposable, dispose, Disposable } from '../../../base/common/lifecycle.js'; import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType, ISCMActionButtonDescriptor } from '../../contrib/scm/common/scm.js'; -import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemGroupDto, SCMHistoryItemDto } from '../common/extHost.protocol.js'; +import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemGroupDto, SCMHistoryItemDto, SCMHistoryItemRefsChangeEventDto } from '../common/extHost.protocol.js'; import { Command } from '../../../editor/common/languages.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { CancellationToken } from '../../../base/common/cancellation.js'; @@ -17,7 +17,7 @@ import { MarshalledId } from '../../../base/common/marshallingIds.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { IMarkdownString } from '../../../base/common/htmlContent.js'; import { IQuickDiffService, QuickDiffProvider } from '../../contrib/scm/common/quickDiff.js'; -import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup, ISCMHistoryItemRef, ISCMHistoryOptions, ISCMHistoryProvider } from '../../contrib/scm/common/history.js'; +import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup, ISCMHistoryItemRef, ISCMHistoryItemRefsChangeEvent, ISCMHistoryOptions, ISCMHistoryProvider } from '../../contrib/scm/common/history.js'; import { ResourceTree } from '../../../base/common/resourceTree.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceContextService } from '../../../platform/workspace/common/workspace.js'; @@ -218,6 +218,9 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { } : undefined; }); + private readonly _historyItemRefChanges = observableValue(this, { added: [], modified: [], removed: [] }); + get historyItemRefChanges(): IObservable { return this._historyItemRefChanges; } + constructor(private readonly proxy: ExtHostSCMShape, private readonly handle: number) { } async resolveHistoryItemRefsCommonAncestor(historyItemRefs: string[]): Promise { @@ -247,6 +250,14 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { $onDidChangeCurrentHistoryItemGroup(historyItemGroup: ISCMHistoryItemGroup | undefined): void { this._currentHistoryItemGroup.set(historyItemGroup, undefined); } + + $onDidChangeHistoryItemRefs(historyItemRefs: SCMHistoryItemRefsChangeEventDto): void { + const added = historyItemRefs.added.map(ref => ({ ...ref, icon: getIconFromIconDto(ref.icon) })); + const modified = historyItemRefs.modified.map(ref => ({ ...ref, icon: getIconFromIconDto(ref.icon) })); + const removed = historyItemRefs.removed.map(ref => ({ ...ref, icon: getIconFromIconDto(ref.icon) })); + + this._historyItemRefChanges.set({ added, modified, removed }, undefined); + } } class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { @@ -484,6 +495,14 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { this._historyProvider.get()?.$onDidChangeCurrentHistoryItemGroup(currentHistoryItemGroup); } + $onDidChangeHistoryProviderHistoryItemRefs(historyItemRefs: SCMHistoryItemRefsChangeEventDto): void { + if (!this.historyProvider.get()) { + return; + } + + this._historyProvider.get()?.$onDidChangeHistoryItemRefs(historyItemRefs); + } + toJSON(): any { return { $mid: MarshalledId.ScmProvider, @@ -728,4 +747,16 @@ export class MainThreadSCM implements MainThreadSCMShape { const provider = repository.provider as MainThreadSCMProvider; provider.$onDidChangeHistoryProviderCurrentHistoryItemGroup(historyItemGroup); } + + async $onDidChangeHistoryProviderHistoryItemRefs(sourceControlHandle: number, historyItemRefs: SCMHistoryItemRefsChangeEventDto): Promise { + await this._repositoryBarriers.get(sourceControlHandle)?.wait(); + const repository = this._repositories.get(sourceControlHandle); + + if (!repository) { + return; + } + + const provider = repository.provider as MainThreadSCMProvider; + provider.$onDidChangeHistoryProviderHistoryItemRefs(historyItemRefs); + } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2f517f05a6d..2055d997aa0 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1559,6 +1559,12 @@ export interface SCMHistoryItemRefDto { readonly icon?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon; } +export interface SCMHistoryItemRefsChangeEventDto { + readonly added: readonly SCMHistoryItemRefDto[]; + readonly modified: readonly SCMHistoryItemRefDto[]; + readonly removed: readonly SCMHistoryItemRefDto[]; +} + export interface SCMHistoryItemDto { readonly id: string; readonly parentIds: string[]; @@ -1601,6 +1607,7 @@ export interface MainThreadSCMShape extends IDisposable { $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): Promise; $onDidChangeHistoryProviderCurrentHistoryItemGroup(sourceControlHandle: number, historyItemGroup: SCMHistoryItemGroupDto | undefined): Promise; + $onDidChangeHistoryProviderHistoryItemRefs(sourceControlHandle: number, historyItemRefs: SCMHistoryItemRefsChangeEventDto): Promise; } export interface MainThreadQuickDiffShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 3976177cc7e..1f15f83d60a 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -597,6 +597,17 @@ class ExtHostSourceControl implements vscode.SourceControl { this._historyProviderCurrentHistoryItemGroup = historyProvider?.currentHistoryItemGroup; this.#proxy.$onDidChangeHistoryProviderCurrentHistoryItemGroup(this.handle, this._historyProviderCurrentHistoryItemGroup); })); + this._historyProviderDisposable.value.add(historyProvider.onDidChangeHistoryItemRefs((e) => { + if (e.added.length === 0 && e.modified.length === 0 && e.removed.length === 0) { + return; + } + + const added = e.added.map(ref => ({ ...ref, icon: getHistoryItemIconDto(ref.icon) })); + const modified = e.modified.map(ref => ({ ...ref, icon: getHistoryItemIconDto(ref.icon) })); + const removed = e.removed.map(ref => ({ ...ref, icon: getHistoryItemIconDto(ref.icon) })); + + this.#proxy.$onDidChangeHistoryProviderHistoryItemRefs(this.handle, { added, modified, removed }); + })); } } diff --git a/src/vs/workbench/contrib/scm/common/history.ts b/src/vs/workbench/contrib/scm/common/history.ts index fbc0d6c0f1f..6a388fcb460 100644 --- a/src/vs/workbench/contrib/scm/common/history.ts +++ b/src/vs/workbench/contrib/scm/common/history.ts @@ -21,6 +21,8 @@ export interface ISCMHistoryProvider { readonly currentHistoryItemRemoteRef: IObservable; readonly currentHistoryItemBaseRef: IObservable; + readonly historyItemRefChanges: IObservable; + provideHistoryItemRefs(): Promise; provideHistoryItems(options: ISCMHistoryOptions): Promise; provideHistoryItemChanges(historyItemId: string, historyItemParentId: string | undefined): Promise; @@ -57,6 +59,12 @@ export interface ISCMHistoryItemRef { readonly icon?: URI | { light: URI; dark: URI } | ThemeIcon; } +export interface ISCMHistoryItemRefsChangeEvent { + readonly added: readonly ISCMHistoryItemRef[]; + readonly removed: readonly ISCMHistoryItemRef[]; + readonly modified: readonly ISCMHistoryItemRef[]; +} + export interface ISCMHistoryItem { readonly id: string; readonly parentIds: string[]; diff --git a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts index d465d599ba2..b6a4893dd54 100644 --- a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts @@ -22,7 +22,7 @@ declare module 'vscode' { /** * Fires when history item refs change */ - onDidChangeHistory: Event; + onDidChangeHistoryItemRefs: Event; provideHistoryItemRefs(token: CancellationToken): ProviderResult; provideHistoryItems(options: SourceControlHistoryOptions, token: CancellationToken): ProviderResult; @@ -78,9 +78,9 @@ declare module 'vscode' { readonly renameUri: Uri | undefined; } - export interface SourceControlHistoryChangeEvent { - readonly added: Iterable; - readonly removed: Iterable; - readonly modified: Iterable; + export interface SourceControlHistoryItemRefsChangeEvent { + readonly added: readonly SourceControlHistoryItemRef[]; + readonly removed: readonly SourceControlHistoryItemRef[]; + readonly modified: readonly SourceControlHistoryItemRef[]; } } From c4d1cc2e6760a40db1244080da9ee7658da07be2 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 9 Sep 2024 19:39:37 -0700 Subject: [PATCH 19/45] Fix GitHub account ids being numbers (#228045) For a long time the account id wasn't handled correctly. It should be a string, but the API returns a number. This ensures it's a string and does some migration logic. --- .../github-authentication/src/github.ts | 25 ++++++++++++++++--- .../github-authentication/src/githubServer.ts | 4 +-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index ed584c65f8b..231c793e6a4 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -18,7 +18,9 @@ interface SessionData { account?: { label?: string; displayName?: string; - id: string; + // Unfortunately, for some time the id was a number, so we need to support both. + // This can be removed once we are confident that all users have migrated to the new id. + id: string | number; }; scopes: string[]; accessToken: string; @@ -239,9 +241,14 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid return []; } + // Unfortunately, we were using a number secretly for the account id for some time... this is due to a bad `any`. + // AuthenticationSession's account id is a string, so we need to detect when there is a number accountId and re-store + // the sessions to migrate away from the bad number usage. + // TODO@TylerLeonhardt: Remove this after we are confident that all users have migrated to the new id. + let seenNumberAccountId: boolean = false; // TODO: eventually remove this Set because we should only have one session per set of scopes. const scopesSeen = new Set(); - const sessionPromises = sessionData.map(async (session: SessionData) => { + const sessionPromises = sessionData.map(async (session: SessionData): Promise => { // For GitHub scope list, order doesn't matter so we immediately sort the scopes const scopesStr = [...session.scopes].sort().join(' '); if (!this._supportsMultipleAccounts && scopesSeen.has(scopesStr)) { @@ -262,13 +269,23 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid this._logger.trace(`Read the following session from the keychain with the following scopes: ${scopesStr}`); scopesSeen.add(scopesStr); + + let accountId: string; + if (session.account?.id) { + if (typeof session.account.id === 'number') { + seenNumberAccountId = true; + } + accountId = `${session.account.id}`; + } else { + accountId = userInfo?.id ?? ''; + } return { id: session.id, account: { label: session.account ? session.account.label ?? session.account.displayName ?? '' : userInfo?.accountName ?? '', - id: session.account?.id ?? userInfo?.id ?? '' + id: accountId }, // we set this to session.scopes to maintain the original order of the scopes requested // by the extension that called getSession() @@ -283,7 +300,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid .filter((p?: T): p is T => Boolean(p)); this._logger.info(`Got ${verifiedSessions.length} verified sessions.`); - if (verifiedSessions.length !== sessionData.length) { + if (seenNumberAccountId || verifiedSessions.length !== sessionData.length) { await this.storeSessions(verifiedSessions); } diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index c9f0a8c07d5..a9f94ccf65d 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -228,9 +228,9 @@ export class GitHubServer implements IGitHubServer { if (result.ok) { try { - const json = await result.json(); + const json = await result.json() as { id: number; login: string }; this._logger.info('Got account info!'); - return { id: json.id, accountName: json.login }; + return { id: `${json.id}`, accountName: json.login }; } catch (e) { this._logger.error(`Unexpected error parsing response from GitHub: ${e.message ?? e}`); throw e; From d0079eb32b7b84ad4c778d28aafae6db1e0ef3ed Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 10 Sep 2024 13:08:18 +1000 Subject: [PATCH 20/45] Collapse unchanged lines in input/metadata editors of NB Diff view (#228051) * Collapse unchanged lines in input/metadata editors of NB Diff view * add comments --- .../browser/diff/diffCellEditorOptions.ts | 8 +- .../notebook/browser/diff/diffComponents.ts | 86 +++++++---- .../browser/diff/diffElementViewModel.ts | 83 ++++++++--- .../browser/diff/notebookDiffActions.ts | 24 ++++ .../browser/diff/notebookDiffEditor.ts | 10 +- .../browser/diff/notebookDiffViewModel.ts | 40 ++++-- .../browser/diff/notebookMultiDiffEditor.ts | 3 +- .../browser/diff/unchangedEditorRegions.ts | 133 ++++++++++++++++++ .../test/browser/notebookDiff.test.ts | 25 ++-- 9 files changed, 335 insertions(+), 77 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/browser/diff/unchangedEditorRegions.ts diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions.ts index a14887cabe9..14c76fe0a75 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions.ts @@ -10,15 +10,19 @@ import { IEditorOptions } from '../../../../../editor/common/config/editorOption * Do not leave at 12, when at 12 and we have whitespace and only one line, * then there's not enough space for the button `Show Whitespace Differences` */ -export const fixedEditorPaddingSingleLineCells = { +const fixedEditorPaddingSingleLineCells = { top: 24, bottom: 24 }; -export const fixedEditorPadding = { +const fixedEditorPadding = { top: 12, bottom: 12 }; +export function getEditorPadding(lineCount: number) { + return lineCount === 1 ? fixedEditorPaddingSingleLineCells : fixedEditorPadding; +} + export const fixedEditorOptions: IEditorOptions = { padding: fixedEditorPadding, scrollBeyondLastLine: false, diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 664df4fa56a..7018c9f126f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -38,7 +38,7 @@ import { IConfigurationService } from '../../../../../platform/configuration/com import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { WorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; -import { fixedDiffEditorOptions, fixedEditorOptions, fixedEditorPadding, fixedEditorPaddingSingleLineCells } from './diffCellEditorOptions.js'; +import { fixedDiffEditorOptions, fixedEditorOptions, getEditorPadding } from './diffCellEditorOptions.js'; import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js'; import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; import { DiffEditorWidget } from '../../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; @@ -48,6 +48,7 @@ import { localize } from '../../../../../nls.js'; import { Emitter } from '../../../../../base/common/event.js'; import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; import { getFormattedMetadataJSON } from '../../common/model/notebookCellTextModel.js'; +import { IDiffEditorOptions } from '../../../../../editor/common/config/editorOptions.js'; export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { return { @@ -516,20 +517,36 @@ abstract class AbstractElementRenderer extends Disposable { modifiedEditor: getOptimizedNestedCodeEditorWidgetOptions() }); + if (this.cell.unchangedRegionsService.options.enabled) { + this._metadataEditor.updateOptions({ hideUnchangedRegions: this.cell.unchangedRegionsService.options }); + } + this._metadataEditorDisposeStore.add(this.cell.unchangedRegionsService.options.onDidChangeEnablement(() => { + if (this._metadataEditor) { + this._metadataEditor.updateOptions({ hideUnchangedRegions: this.cell.unchangedRegionsService.options }); + } + })); + + this.layout({ metadataHeight: true }); this._metadataEditorDisposeStore.add(this._metadataEditor); this._metadataEditorContainer?.classList.add('diff'); - const originalMetadataModel = await this.textModelService.createModelReference(CellUri.generateCellPropertyUri(this.cell.originalDocument.uri, this.cell.original.handle, Schemas.vscodeNotebookCellMetadata)); - const modifiedMetadataModel = await this.textModelService.createModelReference(CellUri.generateCellPropertyUri(this.cell.modifiedDocument.uri, this.cell.modified.handle, Schemas.vscodeNotebookCellMetadata)); - this._metadataEditor.setModel({ + const [originalMetadataModel, modifiedMetadataModel] = await Promise.all([ + this.textModelService.createModelReference(CellUri.generateCellPropertyUri(this.cell.originalDocument.uri, this.cell.original.handle, Schemas.vscodeNotebookCellMetadata)), + this.textModelService.createModelReference(CellUri.generateCellPropertyUri(this.cell.modifiedDocument.uri, this.cell.modified.handle, Schemas.vscodeNotebookCellMetadata)) + ]); + this._metadataEditorDisposeStore.add(originalMetadataModel); + this._metadataEditorDisposeStore.add(modifiedMetadataModel); + const vm = this._metadataEditor.createViewModel({ original: originalMetadataModel.object.textEditorModel, modified: modifiedMetadataModel.object.textEditorModel }); - - this._metadataEditorDisposeStore.add(originalMetadataModel); - this._metadataEditorDisposeStore.add(modifiedMetadataModel); + // Reduces flicker (compute this before setting the model) + // Else when the model is set, the height of the editor will be x, after diff is computed, then height will be y. + // & that results in flicker. + await vm.waitForDiff(); + this._metadataEditor.setModel(vm); this.cell.metadataHeight = this._metadataEditor.getContentHeight(); @@ -847,10 +864,8 @@ abstract class SingleSideDiffElement extends AbstractElementRenderer { this.cell.editorHeight = 0; return; } - - const lineCount = this.nestedCellViewModel.textModel.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; + const editorHeight = this.cell.computeInputEditorHeight(lineHeight); this._editorContainer.style.height = `${editorHeight}px`; this._editorContainer.style.display = 'block'; @@ -1585,7 +1600,7 @@ export class ModifiedElement extends AbstractElementRenderer { const lineCount = modifiedCell.textModel.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = this.cell.layoutInfo.editorHeight !== 0 ? this.cell.layoutInfo.editorHeight : (lineCount * lineHeight) + fixedEditorPadding.top + fixedEditorPadding.bottom; + const editorHeight = this.cell.layoutInfo.editorHeight !== 0 ? this.cell.layoutInfo.editorHeight : this.cell.computeInputEditorHeight(lineHeight); this._editorContainer.style.height = `${editorHeight}px`; this._editorContainer.style.display = 'block'; @@ -1603,11 +1618,17 @@ export class ModifiedElement extends AbstractElementRenderer { // E.g. assume we have a cell with 1 line and we add some whitespace, // Then diff editor displays the button `Show Whitespace Differences`, however with 12 paddings on the top, the // button can get cut off. - if (lineCount === 1) { - this._editor.updateOptions({ - padding: fixedEditorPaddingSingleLineCells - }); + const options: IDiffEditorOptions = { + padding: getEditorPadding(lineCount) + }; + if (this.cell.unchangedRegionsService.options.enabled) { + options.hideUnchangedRegions = this.cell.unchangedRegionsService.options; } + this._editor.updateOptions(options); + this._register(this.cell.unchangedRegionsService.options.onDidChangeEnablement(() => { + options.hideUnchangedRegions = this.cell.unchangedRegionsService.options; + this._editor?.updateOptions(options); + })); this._editor.layout({ width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN, height: editorHeight @@ -1683,25 +1704,28 @@ export class ModifiedElement extends AbstractElementRenderer { } private async _initializeSourceDiffEditor() { - const originalCell = this.cell.original; - const modifiedCell = this.cell.modified; - - const originalRef = await this.textModelService.createModelReference(originalCell.uri); - const modifiedRef = await this.textModelService.createModelReference(modifiedCell.uri); - - if (this._isDisposed) { - return; - } - - const textModel = originalRef.object.textEditorModel; - const modifiedTextModel = modifiedRef.object.textEditorModel; + const [originalRef, modifiedRef] = await Promise.all([ + this.textModelService.createModelReference(this.cell.original.uri), + this.textModelService.createModelReference(this.cell.modified.uri)]); this._register(originalRef); this._register(modifiedRef); - this._editor!.setModel({ - original: textModel, - modified: modifiedTextModel, - }); + if (this._isDisposed) { + originalRef.dispose(); + modifiedRef.dispose(); + return; + } + + const vm = this._register(this._editor!.createViewModel({ + original: originalRef.object.textEditorModel, + modified: modifiedRef.object.textEditorModel, + })); + + // Reduces flicker (compute this before setting the model) + // Else when the model is set, the height of the editor will be x, after diff is computed, then height will be y. + // & that results in flicker. + await vm.waitForDiff(); + this._editor!.setModel(vm); const handleViewStateChange = () => { this._editorViewStateChanged = true; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts index 16c1b24c0c0..9e82e57bee0 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts @@ -10,7 +10,7 @@ import { URI } from '../../../../../base/common/uri.js'; import { DiffEditorWidget } from '../../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; import { FontInfo } from '../../../../../editor/common/config/fontInfo.js'; import * as editorCommon from '../../../../../editor/common/editorCommon.js'; -import { fixedEditorPadding } from './diffCellEditorOptions.js'; +import { getEditorPadding } from './diffCellEditorOptions.js'; import { DiffNestedCellViewModel } from './diffNestedCellViewModel.js'; import { NotebookDiffEditorEventDispatcher, NotebookDiffViewEventType } from './eventDispatcher.js'; import { CellDiffViewModelLayoutChangeEvent, DIFF_CELL_MARGIN, DiffSide, IDiffElementLayoutInfo } from './notebookDiffEditorBrowser.js'; @@ -18,9 +18,16 @@ import { CellLayoutState, IGenericCellViewModel } from '../notebookBrowser.js'; import { NotebookLayoutInfo } from '../notebookViewEvents.js'; import { getFormattedMetadataJSON, NotebookCellTextModel } from '../../common/model/notebookCellTextModel.js'; import { NotebookTextModel } from '../../common/model/notebookTextModel.js'; -import { ICellOutput, INotebookTextModel, IOutputDto, IOutputItemDto } from '../../common/notebookCommon.js'; +import { CellUri, ICellOutput, INotebookTextModel, IOutputDto, IOutputItemDto } from '../../common/notebookCommon.js'; import { INotebookService } from '../../common/notebookService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IUnchangedEditorRegionsService } from './unchangedEditorRegions.js'; +import { Schemas } from '../../../../../base/common/network.js'; + +// From `.monaco-editor .diff-hidden-lines .center` in src/vs/editor/browser/widget/diffEditor/style.css +export const HeightOfHiddenLinesRegionInDiffEditor = 24; + +export const DefaultLineHeight = 17; export enum PropertyFoldingState { Expanded, @@ -204,7 +211,9 @@ export abstract class DiffElementCellViewModelBase extends DiffElementViewModelB fontInfo: FontInfo | undefined; }, notebookService: INotebookService, - private readonly configurationService: IConfigurationService + public readonly index: number, + private readonly configurationService: IConfigurationService, + public readonly unchangedRegionsService: IUnchangedEditorRegionsService ) { super(mainDocumentTextModel, editorEventDispatcher, initData); this.original = original ? this._register(new DiffNestedCellViewModel(original, notebookService)) : undefined; @@ -246,14 +255,14 @@ export abstract class DiffElementCellViewModelBase extends DiffElementViewModelB case 'insert': { const lineCount = this.modified!.textModel.textBuffer.getLineCount(); - const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; + const editorHeight = lineCount * lineHeight + getEditorPadding(lineCount).top + getEditorPadding(lineCount).bottom; return editorHeight; } case 'delete': case 'modified': { const lineCount = this.original!.textModel.textBuffer.getLineCount(); - const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; + const editorHeight = lineCount * lineHeight + getEditorPadding(lineCount).top + getEditorPadding(lineCount).bottom; return editorHeight; } } @@ -364,7 +373,7 @@ export abstract class DiffElementCellViewModelBase extends DiffElementViewModelB getHeight(lineHeight: number) { if (this._layoutInfo.layoutState === CellLayoutState.Uninitialized) { - const editorHeight = this.cellFoldingState === PropertyFoldingState.Collapsed ? 0 : this.estimateEditorHeight(lineHeight); + const editorHeight = this.cellFoldingState === PropertyFoldingState.Collapsed ? 0 : this.computeInputEditorHeight(lineHeight); return this._computeTotalHeight(editorHeight); } else { return this._layoutInfo.totalHeight; @@ -385,15 +394,9 @@ export abstract class DiffElementCellViewModelBase extends DiffElementViewModelB return totalHeight; } - private estimateEditorHeight(lineHeight: number | undefined = 20): number { - const hasScrolling = false; - const verticalScrollbarHeight = hasScrolling ? 12 : 0; // take zoom level into account - // const editorPadding = this.viewContext.notebookOptions.computeEditorPadding(this.internalMetadata); + public computeInputEditorHeight(lineHeight: number): number { const lineCount = Math.max(this.original?.textModel.textBuffer.getLineCount() ?? 1, this.modified?.textModel.textBuffer.getLineCount() ?? 1); - return lineCount * lineHeight - + 24 // Top padding - + 12 // Bottom padding - + verticalScrollbarHeight; + return lineCount * lineHeight + getEditorPadding(lineCount).top + getEditorPadding(lineCount).bottom; } private _getOutputTotalHeight(rawOutputHeight: number, metadataHeight: number) { @@ -474,6 +477,10 @@ export class SideBySideDiffElementViewModel extends DiffElementCellViewModelBase override readonly modified!: DiffNestedCellViewModel; override readonly type: 'unchanged' | 'modified'; + /** + * The height of the editor when the unchanged lines are collapsed. + */ + private editorHeightWithUnchangedLinesCollapsed?: number; constructor( mainDocumentTextModel: NotebookTextModel, readonly otherDocumentTextModel: NotebookTextModel, @@ -487,7 +494,9 @@ export class SideBySideDiffElementViewModel extends DiffElementCellViewModelBase fontInfo: FontInfo | undefined; }, notebookService: INotebookService, - configurationService: IConfigurationService + configurationService: IConfigurationService, + index: number, + unchangedRegionsService: IUnchangedEditorRegionsService ) { super( mainDocumentTextModel, @@ -497,7 +506,9 @@ export class SideBySideDiffElementViewModel extends DiffElementCellViewModelBase editorEventDispatcher, initData, notebookService, - configurationService); + index, + configurationService, + unchangedRegionsService); this.type = type; @@ -629,6 +640,40 @@ export class SideBySideDiffElementViewModel extends DiffElementCellViewModelBase return this.modified; } } + + public override computeInputEditorHeight(lineHeight: number): number { + if (this.type === 'modified' && + typeof this.editorHeightWithUnchangedLinesCollapsed === 'number' && + this.unchangedRegionsService.options.enabled && + this.checkIfInputModified()) { + return this.editorHeightWithUnchangedLinesCollapsed; + } + + return super.computeInputEditorHeight(lineHeight); + } + + private async computeInputEditorHeightWithUnchangedLinesHidden() { + if (this.checkIfInputModified()) { + this.editorHeightWithUnchangedLinesCollapsed = this._layoutInfo.editorHeight = await this.unchangedRegionsService.computeEditorHeight(this.original.uri, this.modified.uri); + } + } + + private async computeMetadataEditorHeightWithUnchangedLinesHidden() { + if (this.checkMetadataIfModified()) { + const originalMetadataUri = CellUri.generateCellPropertyUri(this.originalDocument.uri, this.original.handle, Schemas.vscodeNotebookCellMetadata); + const modifiedMetadataUri = CellUri.generateCellPropertyUri(this.modifiedDocument.uri, this.modified.handle, Schemas.vscodeNotebookCellMetadata); + this._layoutInfo.metadataHeight = await this.unchangedRegionsService.computeEditorHeight(originalMetadataUri, modifiedMetadataUri); + } + } + + public async computeEditorHeights() { + if (this.type === 'unchanged' || !this.unchangedRegionsService.options.enabled) { + return; + } + + await Promise.all([this.computeInputEditorHeightWithUnchangedLinesHidden(), this.computeMetadataEditorHeightWithUnchangedLinesHidden()]); + } + } export class SingleSideDiffElementViewModel extends DiffElementCellViewModelBase { @@ -667,9 +712,11 @@ export class SingleSideDiffElementViewModel extends DiffElementCellViewModelBase fontInfo: FontInfo | undefined; }, notebookService: INotebookService, - configurationService: IConfigurationService + configurationService: IConfigurationService, + unchangedRegionsService: IUnchangedEditorRegionsService, + index: number ) { - super(mainDocumentTextModel, original, modified, type, editorEventDispatcher, initData, notebookService, configurationService); + super(mainDocumentTextModel, original, modified, type, editorEventDispatcher, initData, notebookService, index, configurationService, unchangedRegionsService); this.type = type; this._register(this.cellViewModel.onDidChangeOutputLayout(() => { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index fe0f33e5e75..7c776ef9806 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -61,6 +61,30 @@ registerAction2(class extends Action2 { } }); +registerAction2(class extends Action2 { + constructor() { + super({ + id: 'notebook.diff.cell.toggleCollapseUnchangedRegions', + title: localize2('notebook.diff.cell.toggleCollapseUnchangedRegions', 'Toggle Collapse Unchanged Regions'), + icon: Codicon.map, + toggled: ContextKeyExpr.has('config.diffEditor.hideUnchangedRegions.enabled'), + precondition: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID), + menu: { + id: MenuId.EditorTitle, + group: 'navigation', + when: ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID), + }, + }); + } + + run(accessor: ServicesAccessor, ...args: unknown[]): void { + const configurationService = accessor.get(IConfigurationService); + const newValue = !configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + configurationService.updateValue('diffEditor.hideUnchangedRegions.enabled', newValue); + } +}); + + registerAction2(class extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index 79710bf55df..dadcdba8537 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -46,6 +46,10 @@ import { NotebookDiffOverviewRuler } from './notebookDiffOverviewRuler.js'; import { registerZIndex, ZIndex } from '../../../../../platform/layout/browser/zIndexRegistry.js'; import { NotebookDiffViewModel } from './notebookDiffViewModel.js'; import { INotebookService } from '../../common/notebookService.js'; +import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; +import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { UnchangedEditorRegionsService } from './unchangedEditorRegions.js'; const $ = DOM.$; @@ -150,6 +154,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @INotebookService private readonly notebookService: INotebookService, + @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, + @ITextModelService private readonly textModelResolverService: ITextModelService, + @ITextResourceConfigurationService private readonly textConfigurationService: ITextResourceConfigurationService ) { super(NotebookTextDiffEditor.ID, group, telemetryService, themeService, storageService); this._notebookOptions = instantiationService.createInstance(NotebookOptions, this.window, false, undefined); @@ -511,7 +518,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD })); if (this._model) { - const vm = this.notebookDiffViewModel = this._register(new NotebookDiffViewModel(this._model, this.notebookEditorWorkerService, this.configurationService, this._eventDispatcher!, this.notebookService, this.fontInfo)); + const unchangedEditorRegions = this._localStore.add(new UnchangedEditorRegionsService(this.configurationService, this.editorWorkerService, this.textModelResolverService, this.textConfigurationService, this.fontInfo.lineHeight)); + const vm = this.notebookDiffViewModel = this._register(new NotebookDiffViewModel(this._model, this.notebookEditorWorkerService, this.configurationService, this._eventDispatcher!, this.notebookService, unchangedEditorRegions, this.fontInfo, undefined)); this._localStore.add(this.notebookDiffViewModel.onDidChangeItems(e => { this._list.splice(e.start, e.deleteCount, e.elements); if (this.isOverviewRulerEnabled()) { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts index 0d5c1f6fe1d..6c22cefa0ce 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts @@ -20,6 +20,7 @@ import { NotebookTextModel } from '../../common/model/notebookTextModel.js'; import { CellUri, INotebookDiffEditorModel, INotebookDiffResult } from '../../common/notebookCommon.js'; import { INotebookService } from '../../common/notebookService.js'; import { INotebookEditorWorkerService } from '../../common/services/notebookWorkerService.js'; +import { IUnchangedEditorRegionsService } from './unchangedEditorRegions.js'; export class NotebookDiffViewModel extends Disposable implements INotebookDiffViewModel, IValueWithChangeEvent { private readonly placeholderAndRelatedCells = new Map(); @@ -76,6 +77,7 @@ export class NotebookDiffViewModel extends Disposable implements INotebookDiffVi private readonly configurationService: IConfigurationService, private readonly eventDispatcher: NotebookDiffEditorEventDispatcher, private readonly notebookService: INotebookService, + private readonly unchangedRegionsService: IUnchangedEditorRegionsService, private readonly fontInfo?: FontInfo, private readonly excludeUnchangedPlaceholder?: boolean, ) { @@ -134,7 +136,7 @@ export class NotebookDiffViewModel extends Disposable implements INotebookDiffVi if (isEqual(cellDiffInfo, this.originalCellViewModels, this.model)) { return; } else { - this.updateViewModels(cellDiffInfo); + await this.updateViewModels(cellDiffInfo); this.updateDiffEditorItems(); return { firstChangeIndex }; } @@ -192,8 +194,8 @@ export class NotebookDiffViewModel extends Disposable implements INotebookDiffVi this._onDidChange.fire(); } - private updateViewModels(cellDiffInfo: CellDiffInfo[]) { - const cellViewModels = createDiffViewModels(this.configurationService, this.model, this.eventDispatcher, cellDiffInfo, this.fontInfo, this.notebookService); + private async updateViewModels(cellDiffInfo: CellDiffInfo[]) { + const cellViewModels = await createDiffViewModels(this.configurationService, this.model, this.eventDispatcher, cellDiffInfo, this.fontInfo, this.notebookService, this.unchangedRegionsService); const oldLength = this._items.length; this.clear(); this._items.splice(0, oldLength); @@ -237,6 +239,8 @@ export class NotebookDiffViewModel extends Disposable implements INotebookDiffVi } }); + // Note, ensure all of the height calculations are done before firing the event. + // This is to ensure that the diff editor is not resized multiple times, thereby avoiding flickering. this._onDidChangeItems.fire({ start: 0, deleteCount: oldLength, elements: this._items }); } } @@ -391,7 +395,7 @@ function isEqual(cellDiffInfo: CellDiffInfo[], viewModels: DiffElementCellViewMo return true; } -function createDiffViewModels(configurationService: IConfigurationService, model: INotebookDiffEditorModel, eventDispatcher: NotebookDiffEditorEventDispatcher, computedCellDiffs: CellDiffInfo[], fontInfo: FontInfo | undefined, notebookService: INotebookService) { +async function createDiffViewModels(configurationService: IConfigurationService, model: INotebookDiffEditorModel, eventDispatcher: NotebookDiffEditorEventDispatcher, computedCellDiffs: CellDiffInfo[], fontInfo: FontInfo | undefined, notebookService: INotebookService, unchangedRegionsService: IUnchangedEditorRegionsService) { const originalModel = model.original.notebook; const modifiedModel = model.modified.notebook; const initData = { @@ -399,8 +403,7 @@ function createDiffViewModels(configurationService: IConfigurationService, model outputStatusHeight: configurationService.getValue('notebook.diff.ignoreOutputs') || !!(modifiedModel.transientOptions.transientOutputs) ? 0 : 25, fontInfo }; - - return computedCellDiffs.map(diff => { + return Promise.all(computedCellDiffs.map(async (diff) => { switch (diff.type) { case 'delete': { return new SingleSideDiffElementViewModel( @@ -412,7 +415,9 @@ function createDiffViewModels(configurationService: IConfigurationService, model eventDispatcher, initData, notebookService, - configurationService + configurationService, + unchangedRegionsService, + diff.originalCellIndex ); } case 'insert': { @@ -425,11 +430,13 @@ function createDiffViewModels(configurationService: IConfigurationService, model eventDispatcher, initData, notebookService, - configurationService + configurationService, + unchangedRegionsService, + diff.modifiedCellIndex ); } case 'modified': { - return new SideBySideDiffElementViewModel( + const viewModel = new SideBySideDiffElementViewModel( model.modified.notebook, model.original.notebook, originalModel.cells[diff.originalCellIndex], @@ -438,8 +445,15 @@ function createDiffViewModels(configurationService: IConfigurationService, model eventDispatcher, initData, notebookService, - configurationService + configurationService, + diff.originalCellIndex, + unchangedRegionsService ); + // Reduces flicker (compute this before setting the model) + // Else when the model is set, the height of the editor will be x, after diff is computed, then height will be y. + // & that results in flicker. + await viewModel.computeEditorHeights(); + return viewModel; } case 'unchanged': { return new SideBySideDiffElementViewModel( @@ -450,11 +464,13 @@ function createDiffViewModels(configurationService: IConfigurationService, model 'unchanged', eventDispatcher, initData, notebookService, - configurationService + configurationService, + diff.originalCellIndex, + unchangedRegionsService ); } } - }); + })); } function computeModifiedLCS(change: IDiffChange, originalModel: NotebookTextModel, modifiedModel: NotebookTextModel) { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts index 7f6f35699d0..3df5bc39b5e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts @@ -39,6 +39,7 @@ import type { DocumentDiffItemViewModel, MultiDiffEditorViewModel } from '../../ import type { URI } from '../../../../../base/common/uri.js'; import { type IDiffElementViewModelBase } from './diffElementViewModel.js'; import { autorun, transaction } from '../../../../../base/common/observable.js'; +import { UnchangedEditorRegionsService } from './unchangedEditorRegions.js'; export class NotebookMultiTextDiffEditor extends EditorPane { private _multiDiffEditorWidget?: MultiDiffEditorWidget; @@ -111,7 +112,7 @@ export class NotebookMultiTextDiffEditor extends EditorPane { this._model = model; } const eventDispatcher = this.modelSpecificResources.add(new NotebookDiffEditorEventDispatcher()); - this.viewModel = this.modelSpecificResources.add(new NotebookDiffViewModel(model, this.notebookEditorWorkerService, this.configurationService, eventDispatcher, this.notebookService, undefined, true)); + this.viewModel = this.modelSpecificResources.add(new NotebookDiffViewModel(model, this.notebookEditorWorkerService, this.configurationService, eventDispatcher, this.notebookService, UnchangedEditorRegionsService.Empty, undefined, true)); await this.viewModel.computeDiff(this.modelSpecificResources.add(new CancellationTokenSource()).token); this.ctxHasUnchangedCells.set(this.viewModel.hasUnchangedCells); this.ctxHasUnchangedCells.set(this.viewModel.hasUnchangedCells); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/unchangedEditorRegions.ts b/src/vs/workbench/contrib/notebook/browser/diff/unchangedEditorRegions.ts new file mode 100644 index 00000000000..4d039c03b4e --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/unchangedEditorRegions.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from '../../../../../base/common/event.js'; +import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { UnchangedRegion } from '../../../../../editor/browser/widget/diffEditor/diffEditorViewModel.js'; +import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; +import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { getEditorPadding } from './diffCellEditorOptions.js'; +import { HeightOfHiddenLinesRegionInDiffEditor } from './diffElementViewModel.js'; + +export type UnchangedEditorRegionOptions = { + enabled: boolean; + contextLineCount: number; + minimumLineCount: number; + revealLineCount: number; + onDidChangeEnablement: Event; +}; + +export interface IUnchangedEditorRegionsService { + readonly options: Readonly; + + /** + * Given two URIs, compute the height of the editor with unchanged regions collapsed. + * @param originalUri + * @param modifiedUri + */ + computeEditorHeight(originalUri: URI, modifiedUri: URI): Promise; +} + +export class UnchangedEditorRegionsService extends Disposable implements IUnchangedEditorRegionsService { + public readonly options: Readonly; + constructor(configurationService: IConfigurationService, + private readonly editorWorkerService: IEditorWorkerService, + private readonly textModelResolverService: ITextModelService, + private readonly textConfigurationService: ITextResourceConfigurationService, + private readonly lineHeight: number + ) { + super(); + this.options = this._register(createHideUnchangedRegionOptions(configurationService)); + } + + public static Empty: IUnchangedEditorRegionsService = { + options: { + enabled: false, + contextLineCount: 0, + minimumLineCount: 0, + revealLineCount: 0, + onDidChangeEnablement: Event.None, + }, + computeEditorHeight: (_originalUri: URI, _modifiedUri: URI) => Promise.resolve(0) + }; + + public async computeEditorHeight( + originalUri: URI, + modifiedUri: URI) { + const { numberOfUnchangedRegions, numberOfVisibleLines } = await computeInputUnchangedLines(originalUri, modifiedUri, this.options, this.editorWorkerService, this.textModelResolverService, this.textConfigurationService); + const lineCount = numberOfVisibleLines; + const unchangeRegionsHeight = numberOfUnchangedRegions * HeightOfHiddenLinesRegionInDiffEditor; + // TODO: When we have a horizontal scrollbar, we need to add 12 to the height. + // Right now there's no way to determine if a horizontal scrollbar is visible in the editor. + return lineCount * this.lineHeight + getEditorPadding(lineCount).top + getEditorPadding(lineCount).bottom + unchangeRegionsHeight; + + } +} + +function createHideUnchangedRegionOptions(configurationService: IConfigurationService): UnchangedEditorRegionOptions & { dispose: () => void } { + const disposables = new DisposableStore(); + const unchangedRegionsEnablementEmitter = disposables.add(new Emitter()); + + const options = { + enabled: configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'), + minimumLineCount: configurationService.getValue('diffEditor.hideUnchangedRegions.minimumLineCount'), + contextLineCount: configurationService.getValue('diffEditor.hideUnchangedRegions.contextLineCount'), + revealLineCount: configurationService.getValue('diffEditor.hideUnchangedRegions.revealLineCount'), + // We only care about enable/disablement. + // If user changes counters when a diff editor is open, we do not care, might as well ask user to reload. + // Simpler and almost never going to happen. + onDidChangeEnablement: unchangedRegionsEnablementEmitter.event.bind(unchangedRegionsEnablementEmitter), + dispose: () => disposables.dispose() + }; + + disposables.add(configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('diffEditor.hideUnchangedRegions.enabled')) { + options.enabled = configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + unchangedRegionsEnablementEmitter.fire(options.enabled); + } + + })); + + return options; +} + +async function computeInputUnchangedLines(originalUri: URI, + modifiedUri: URI, + unchangedRegionOptions: UnchangedEditorRegionOptions, + editorWorkerService: IEditorWorkerService, + textModelResolverService: ITextModelService, + textConfigurationService: ITextResourceConfigurationService +) { + // Ensure we have resolved the cell text models. + const [originalModel, modifiedModel] = await Promise.all([textModelResolverService.createModelReference(originalUri), textModelResolverService.createModelReference(modifiedUri)]); + + try { + const ignoreTrimWhitespace = textConfigurationService.getValue(originalUri, 'diffEditor.ignoreTrimWhitespace'); + const diff = await editorWorkerService.computeDiff(originalUri, modifiedUri, { + ignoreTrimWhitespace, + maxComputationTimeMs: 0, + computeMoves: false + }, 'advanced'); + const originalLineCount = originalModel.object.textEditorModel.getLineCount(); + const modifiedLineCount = modifiedModel.object.textEditorModel.getLineCount(); + const unchanged = diff ? UnchangedRegion.fromDiffs(diff.changes, + originalLineCount, + modifiedLineCount, + unchangedRegionOptions.minimumLineCount ?? 3, + unchangedRegionOptions.contextLineCount ?? 3) : []; + + const totalLines = Math.max(originalLineCount, modifiedLineCount); + const numberOfUnchangedRegions = unchanged.length; + const numberOfVisibleLines = totalLines - unchanged.reduce((prev, curr) => prev + curr.lineCount, 0); + return { numberOfUnchangedRegions, numberOfVisibleLines }; + } finally { + originalModel.dispose(); + modifiedModel.dispose(); + } +} + diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookDiff.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookDiff.test.ts index e9d86215e56..39a5468f12f 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookDiff.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookDiff.test.ts @@ -20,6 +20,7 @@ import { CellKind, INotebookTextModel } from '../../common/notebookCommon.js'; import { INotebookService } from '../../common/notebookService.js'; import { INotebookEditorWorkerService } from '../../common/services/notebookWorkerService.js'; import { withTestNotebookDiffModel } from './testNotebookEditor.js'; +import { UnchangedEditorRegionsService } from '../../browser/diff/unchangedEditorRegions.js'; class CellSequence implements ISequence { @@ -87,7 +88,7 @@ suite('NotebookDiff', () => { modifiedLength: 1 }]); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); await diffViewModel.computeDiff(token); assert.strictEqual(diffViewModel.items.length, 1); @@ -116,7 +117,7 @@ suite('NotebookDiff', () => { modifiedLength: 1 }]); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); await diffViewModel.computeDiff(token); await verifyChangeEventIsNotFired(diffViewModel); @@ -146,7 +147,7 @@ suite('NotebookDiff', () => { modifiedLength: 1 }]); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); let eventArgs: INotebookDiffViewModelUpdateEvent | undefined = undefined; disposables.add(diffViewModel.onDidChangeItems(e => eventArgs = e)); await diffViewModel.computeDiff(token); @@ -195,7 +196,7 @@ suite('NotebookDiff', () => { modifiedLength: 1 }]); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); await diffViewModel.computeDiff(token); assert.strictEqual(diffViewModel.items.length, 1); @@ -234,7 +235,7 @@ suite('NotebookDiff', () => { modifiedLength: 1 }]); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); await diffViewModel.computeDiff(token); assert.strictEqual(diffViewModel.items.length, 1); @@ -257,7 +258,7 @@ suite('NotebookDiff', () => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); diffResult = diff.ComputeDiff(false); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); let eventArgs: INotebookDiffViewModelUpdateEvent | undefined = undefined; disposables.add(diffViewModel.onDidChangeItems(e => eventArgs = e)); await diffViewModel.computeDiff(token); @@ -287,7 +288,7 @@ suite('NotebookDiff', () => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); diffResult = diff.ComputeDiff(false); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); let eventArgs: INotebookDiffViewModelUpdateEvent | undefined = undefined; disposables.add(diffViewModel.onDidChangeItems(e => eventArgs = e)); await diffViewModel.computeDiff(token); @@ -325,7 +326,7 @@ suite('NotebookDiff', () => { quitEarly: false }; - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); let eventArgs: INotebookDiffViewModelUpdateEvent | undefined = undefined; disposables.add(diffViewModel.onDidChangeItems(e => eventArgs = e)); const result = await diffViewModel.computeDiff(token); @@ -379,7 +380,7 @@ suite('NotebookDiff', () => { quitEarly: false }; - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); let eventArgs: INotebookDiffViewModelUpdateEvent | undefined = undefined; disposables.add(diffViewModel.onDidChangeItems(e => eventArgs = e)); const result = await diffViewModel.computeDiff(token); @@ -441,7 +442,7 @@ suite('NotebookDiff', () => { quitEarly: false }; - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); let eventArgs: INotebookDiffViewModelUpdateEvent | undefined = undefined; disposables.add(diffViewModel.onDidChangeItems(e => eventArgs = e)); await diffViewModel.computeDiff(token); @@ -611,7 +612,7 @@ suite('NotebookDiff', () => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); diffResult = diff.ComputeDiff(false); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); await diffViewModel.computeDiff(token); assert.strictEqual(diffViewModel.items.length, 2); @@ -635,7 +636,7 @@ suite('NotebookDiff', () => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); diffResult = diff.ComputeDiff(false); - diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), undefined)); + diffViewModel = disposables.add(new NotebookDiffViewModel(model, notebookEditorWorkerService, configurationService, eventDispatcher, accessor.get(INotebookService), UnchangedEditorRegionsService.Empty, undefined)); await diffViewModel.computeDiff(token); assert.strictEqual(diffViewModel.items.length, 2); From bffae5151e5d744751187869167958d981872441 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Tue, 10 Sep 2024 06:11:16 +0200 Subject: [PATCH 21/45] fix: memory leak in debug view (#225334) * fix: memory leak in debug view * Organize imports * fix: dispose element disposables * automatically dispose element disposables * simplify code * simplify code * format code --------- Co-authored-by: Rob Lourens --- .../contrib/debug/browser/breakpointsView.ts | 190 ++++++++++++------ 1 file changed, 125 insertions(+), 65 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index a5e9075c983..58dd961da31 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -20,7 +20,7 @@ import { RunOnceScheduler } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; -import { DisposableStore, IDisposable, dispose } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, dispose } from '../../../../base/common/lifecycle.js'; import * as resources from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { Constants } from '../../../../base/common/uint.js'; @@ -39,6 +39,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { WorkbenchList } from '../../../../platform/list/browser/listService.js'; +import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; @@ -48,22 +49,21 @@ import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js'; import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js'; import { IEditorPane } from '../../../common/editor.js'; import { IViewDescriptorService } from '../../../common/views.js'; -import * as icons from './debugIcons.js'; -import { DisassemblyView } from './disassemblyView.js'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; +import { IViewsService } from '../../../services/views/common/viewsService.js'; import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_HAS_MODES, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED, DEBUG_SCHEME, DataBreakpointSetType, DataBreakpointSource, DebuggerString, IBaseBreakpoint, IBreakpoint, IBreakpointEditorContribution, IBreakpointUpdateData, IDataBreakpoint, IDataBreakpointInfoResponse, IDebugModel, IDebugService, IEnablement, IExceptionBreakpoint, IFunctionBreakpoint, IInstructionBreakpoint, State } from '../common/debug.js'; import { Breakpoint, DataBreakpoint, ExceptionBreakpoint, FunctionBreakpoint, InstructionBreakpoint } from '../common/debugModel.js'; import { DisassemblyViewInput } from '../common/disassemblyViewInput.js'; -import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; -import { INotificationService } from '../../../../platform/notification/common/notification.js'; -import { IViewsService } from '../../../services/views/common/viewsService.js'; +import * as icons from './debugIcons.js'; +import { DisassemblyView } from './disassemblyView.js'; const $ = dom.$; -function createCheckbox(disposables: IDisposable[]): HTMLInputElement { +function createCheckbox(disposables: DisposableStore): HTMLInputElement { const checkbox = $('input'); checkbox.type = 'checkbox'; checkbox.tabIndex = -1; - disposables.push(Gesture.ignoreTarget(checkbox)); + disposables.add(Gesture.ignoreTarget(checkbox)); return checkbox; } @@ -432,7 +432,8 @@ interface IBaseBreakpointTemplateData { checkbox: HTMLInputElement; context: BreakpointItem; actionBar: ActionBar; - toDispose: IDisposable[]; + templateDisposables: DisposableStore; + elementDisposables: DisposableStore; badge: HTMLElement; } @@ -466,7 +467,8 @@ interface IFunctionBreakpointInputTemplateData { checkbox: HTMLInputElement; icon: HTMLElement; breakpoint: IFunctionBreakpoint; - toDispose: IDisposable[]; + templateDisposables: DisposableStore; + elementDisposables: DisposableStore; type: 'hitCount' | 'condition' | 'name'; updating?: boolean; } @@ -476,7 +478,8 @@ interface IDataBreakpointInputTemplateData { checkbox: HTMLInputElement; icon: HTMLElement; breakpoint: IDataBreakpoint; - toDispose: IDisposable[]; + elementDisposables: DisposableStore; + templateDisposables: DisposableStore; type: 'hitCount' | 'condition' | 'name'; updating?: boolean; } @@ -485,7 +488,8 @@ interface IExceptionBreakpointInputTemplateData { inputBox: InputBox; checkbox: HTMLInputElement; breakpoint: IExceptionBreakpoint; - toDispose: IDisposable[]; + templateDisposables: DisposableStore; + elementDisposables: DisposableStore; } const breakpointIdToActionBarDomeNode = new Map(); @@ -511,14 +515,16 @@ class BreakpointsRenderer implements IListRenderer { + data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); })); @@ -529,7 +535,7 @@ class BreakpointsRenderer implements IListRenderer { + data.checkbox = createCheckbox(data.templateDisposables); + data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); })); @@ -613,7 +627,7 @@ class ExceptionBreakpointsRenderer implements IListRenderer { + data.checkbox = createCheckbox(data.templateDisposables); + data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); })); @@ -689,7 +709,7 @@ class FunctionBreakpointsRenderer implements IListRenderer { + data.checkbox = createCheckbox(data.templateDisposables); + data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); })); @@ -778,7 +804,7 @@ class DataBreakpointsRenderer implements IListRenderer { + data.checkbox = createCheckbox(data.templateDisposables); + data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); })); @@ -870,7 +902,7 @@ class InstructionBreakpointsRenderer implements IListRenderer { template.updating = true; try { @@ -967,7 +1006,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { + toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { const isEscape = e.equals(KeyCode.Escape); const isEnter = e.equals(KeyCode.Enter); if (isEscape || isEnter) { @@ -976,14 +1015,16 @@ class FunctionBreakpointInputRenderer implements IListRenderer { + toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { if (!template.updating) { wrapUp(!!inputBox.value); } })); template.inputBox = inputBox; - template.toDispose = toDispose; + template.elementDisposables = new DisposableStore(); + template.templateDisposables = toDispose; + template.templateDisposables.add(template.elementDisposables); return template; } @@ -993,7 +1034,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { template.updating = true; @@ -1076,7 +1122,7 @@ class DataBreakpointInputRenderer implements IListRenderer { + toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { const isEscape = e.equals(KeyCode.Escape); const isEnter = e.equals(KeyCode.Enter); if (isEscape || isEnter) { @@ -1085,14 +1131,16 @@ class DataBreakpointInputRenderer implements IListRenderer { + toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { if (!template.updating) { wrapUp(!!inputBox.value); } })); template.inputBox = inputBox; - template.toDispose = toDispose; + template.elementDisposables = new DisposableStore(); + template.templateDisposables = toDispose; + template.templateDisposables.add(template.elementDisposables); return template; } @@ -1102,7 +1150,7 @@ class DataBreakpointInputRenderer implements IListRenderer { this.view.breakpointInputFocused.set(false); let newCondition = template.breakpoint.condition; @@ -1172,7 +1226,7 @@ class ExceptionBreakpointInputRenderer implements IListRenderer { + toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { const isEscape = e.equals(KeyCode.Escape); const isEnter = e.equals(KeyCode.Enter); if (isEscape || isEnter) { @@ -1181,7 +1235,7 @@ class ExceptionBreakpointInputRenderer implements IListRenderer { + toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => { // Need to react with a timeout on the blur event due to possible concurent splices #56443 setTimeout(() => { wrapUp(true); @@ -1189,7 +1243,9 @@ class ExceptionBreakpointInputRenderer implements IListRenderer Date: Tue, 10 Sep 2024 14:30:10 +1000 Subject: [PATCH 22/45] Use relative imports in cli.ts to import the cliProcessMaiin (#228044) * Use relative imports in CLI * ignore joins * revert to prevent inlining --- src/vs/code/node/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index ffd48670edc..b25377a804d 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -117,7 +117,7 @@ export async function main(argv: string[]): Promise { // Extensions Management else if (shouldSpawnCliProcess(args)) { - const cli = await import(['vs', 'code', 'node', 'cliProcessMain'].join('/') /* TODO@esm workaround to prevent esbuild from inlining this */); + const cli = await import(['./cliProcessMain.js'].join('/') /* TODO@esm workaround to prevent esbuild from inlining this */); await cli.main(args); return; From 789c320a1c67bb0547cdef2b8b82634bf93929a3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 10 Sep 2024 07:43:29 +0200 Subject: [PATCH 23/45] Replace mkdirp with fs.mkdirSync(path, { recursive: true }) (#228017) * Replace mkdirp with fs.mkdirSync(path, { recursive: true }) (fix #227931) * compile --- build/gulpfile.vscode.win32.js | 3 +-- build/lib/builtInExtensions.js | 3 +-- build/lib/builtInExtensions.ts | 4 +--- build/package-lock.json | 23 ------------------ build/package.json | 2 -- cglicenses.json | 27 ---------------------- package-lock.json | 1 - package.json | 1 - test/automation/package-lock.json | 22 ------------------ test/automation/package.json | 2 -- test/automation/src/electron.ts | 4 ++-- test/automation/src/playwrightBrowser.ts | 4 ++-- test/integration/browser/package-lock.json | 10 -------- test/integration/browser/package.json | 1 - test/smoke/package-lock.json | 22 ------------------ test/smoke/package.json | 2 -- test/smoke/src/main.ts | 9 ++++---- 17 files changed, 11 insertions(+), 129 deletions(-) diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 5adfdfbfe18..98175f530dd 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -16,7 +16,6 @@ const pkg = require('../package.json'); const product = require('../product.json'); const vfs = require('vinyl-fs'); const rcedit = require('rcedit'); -const mkdirp = require('mkdirp'); const repoPath = path.dirname(__dirname); const buildPath = (/** @type {string} */ arch) => path.join(path.dirname(repoPath), `VSCode-win32-${arch}`); @@ -75,7 +74,7 @@ function buildWin32Setup(arch, target) { const sourcePath = buildPath(arch); const outputPath = setupDir(arch, target); - mkdirp.sync(outputPath); + fs.mkdirSync(outputPath, { recursive: true }); const originalProductJsonPath = path.join(sourcePath, 'resources/app/product.json'); const productJsonPath = path.join(outputPath, 'product.json'); diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index 463ce16e18d..ac784c03506 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -16,7 +16,6 @@ const vfs = require("vinyl-fs"); const ext = require("./extensions"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); -const mkdirp = require('mkdirp'); const root = path.dirname(path.dirname(__dirname)); const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); const builtInExtensions = productjson.builtInExtensions || []; @@ -107,7 +106,7 @@ function readControlFile() { } } function writeControlFile(control) { - mkdirp.sync(path.dirname(controlFilePath)); + fs.mkdirSync(path.dirname(controlFilePath), { recursive: true }); fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } function getBuiltInExtensions() { diff --git a/build/lib/builtInExtensions.ts b/build/lib/builtInExtensions.ts index fefed436bb9..8b831d42d44 100644 --- a/build/lib/builtInExtensions.ts +++ b/build/lib/builtInExtensions.ts @@ -15,8 +15,6 @@ import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; import { Stream } from 'stream'; -const mkdirp = require('mkdirp'); - export interface IExtensionDefinition { name: string; version: string; @@ -147,7 +145,7 @@ function readControlFile(): IControlFile { } function writeControlFile(control: IControlFile): void { - mkdirp.sync(path.dirname(controlFilePath)); + fs.mkdirSync(path.dirname(controlFilePath), { recursive: true }); fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } diff --git a/build/package-lock.json b/build/package-lock.json index 9c423af3291..4499f7cf541 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -31,7 +31,6 @@ "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", "@types/minimist": "^1.2.1", - "@types/mkdirp": "^1.0.1", "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/pump": "^1.0.1", @@ -54,7 +53,6 @@ "gulp-sort": "^2.0.0", "jsonc-parser": "^2.3.0", "mime": "^1.4.1", - "mkdirp": "^1.0.4", "source-map": "0.6.1", "ternary-stream": "^3.0.0", "through2": "^4.0.2", @@ -1124,15 +1122,6 @@ "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", "dev": true }, - "node_modules/@types/mkdirp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", - "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -3353,18 +3342,6 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "devOptional": true }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", diff --git a/build/package.json b/build/package.json index 5e637eaebf4..7017b9144e0 100644 --- a/build/package.json +++ b/build/package.json @@ -25,7 +25,6 @@ "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", "@types/minimist": "^1.2.1", - "@types/mkdirp": "^1.0.1", "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/pump": "^1.0.1", @@ -48,7 +47,6 @@ "gulp-sort": "^2.0.0", "jsonc-parser": "^2.3.0", "mime": "^1.4.1", - "mkdirp": "^1.0.4", "source-map": "0.6.1", "ternary-stream": "^3.0.0", "through2": "^4.0.2", diff --git a/cglicenses.json b/cglicenses.json index d75a53bf172..0b4e03e502b 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -241,33 +241,6 @@ "CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] }, - { - // Reason: The substack org has been deleted on GH - "name": "mkdirp", - "fullLicenseText": [ - "Copyright 2010 James Halliday (mail@substack.net)", - "", - "This project is free software released under the MIT/X11 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:", - "", - "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." - ] - }, { // Reason: repo URI is wrong on crate, pending https://github.com/warp-tech/russh/pull/53 "name": "russh-cryptovec", diff --git a/package-lock.json b/package-lock.json index f4a1e6017c2..de67f93ba3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -131,7 +131,6 @@ "mime": "^1.4.1", "minimatch": "^3.0.4", "minimist": "^1.2.6", - "mkdirp": "^1.0.4", "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", diff --git a/package.json b/package.json index 138ae5f7954..70050be5067 100644 --- a/package.json +++ b/package.json @@ -193,7 +193,6 @@ "mime": "^1.4.1", "minimatch": "^3.0.4", "minimist": "^1.2.6", - "mkdirp": "^1.0.4", "mocha": "^10.2.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", diff --git a/test/automation/package-lock.json b/test/automation/package-lock.json index 1a59eaa65f1..0253b826daf 100644 --- a/test/automation/package-lock.json +++ b/test/automation/package-lock.json @@ -9,14 +9,12 @@ "version": "1.71.0", "license": "MIT", "dependencies": { - "mkdirp": "^1.0.4", "ncp": "^2.0.0", "tmp": "0.2.1", "tree-kill": "1.2.2", "vscode-uri": "3.0.2" }, "devDependencies": { - "@types/mkdirp": "^1.0.1", "@types/ncp": "2.0.1", "@types/node": "20.x", "@types/tmp": "0.2.2", @@ -25,15 +23,6 @@ "watch": "^1.0.2" } }, - "node_modules/@types/mkdirp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", - "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/ncp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/ncp/-/ncp-2.0.1.tgz", @@ -596,17 +585,6 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/test/automation/package.json b/test/automation/package.json index b9dbbec4bb8..4d5dbd02f63 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -18,14 +18,12 @@ "prepublishOnly": "npm run copy-package-version" }, "dependencies": { - "mkdirp": "^1.0.4", "ncp": "^2.0.0", "tmp": "0.2.1", "tree-kill": "1.2.2", "vscode-uri": "3.0.2" }, "devDependencies": { - "@types/mkdirp": "^1.0.1", "@types/ncp": "2.0.1", "@types/node": "20.x", "@types/tmp": "0.2.2", diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index da2087a8faa..8a9a73974f6 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { join } from 'path'; -import * as mkdirp from 'mkdirp'; +import * as fs from 'fs'; import { copyExtension } from './extensions'; import { URI } from 'vscode-uri'; import { measureAndLog } from './logger'; @@ -70,7 +70,7 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom } args.push('--enable-proposed-api=vscode.vscode-test-resolver'); const remoteDataDir = `${userDataDir}-server`; - mkdirp.sync(remoteDataDir); + fs.mkdirSync(remoteDataDir, { recursive: true }); env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir; env['TESTRESOLVER_LOGS_FOLDER'] = join(logsPath, 'server'); diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index 0a98250767b..e36d070cdc9 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -6,7 +6,7 @@ import * as playwright from '@playwright/test'; import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; -import * as mkdirp from 'mkdirp'; +import * as fs from 'fs'; import { URI } from 'vscode-uri'; import { Logger, measureAndLog } from './logger'; import type { LaunchOptions } from './code'; @@ -35,7 +35,7 @@ async function launchServer(options: LaunchOptions) { const serverLogsPath = join(logsPath, 'server'); const codeServerPath = codePath ?? process.env.VSCODE_REMOTE_SERVER_PATH; const agentFolder = userDataDir; - await measureAndLog(() => mkdirp(agentFolder), `mkdirp(${agentFolder})`, logger); + await measureAndLog(() => fs.promises.mkdir(agentFolder, { recursive: true }), `mkdirp(${agentFolder})`, logger); const env = { VSCODE_REMOTE_SERVER_PATH: codeServerPath, diff --git a/test/integration/browser/package-lock.json b/test/integration/browser/package-lock.json index ec66b5554e9..bdec916fb4b 100644 --- a/test/integration/browser/package-lock.json +++ b/test/integration/browser/package-lock.json @@ -9,7 +9,6 @@ "version": "0.1.0", "license": "MIT", "devDependencies": { - "@types/mkdirp": "^1.0.1", "@types/node": "20.x", "@types/rimraf": "^2.0.4", "@types/tmp": "0.1.0", @@ -42,15 +41,6 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, - "node_modules/@types/mkdirp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", - "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { "version": "20.11.24", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", diff --git a/test/integration/browser/package.json b/test/integration/browser/package.json index e87c8669983..728d02763d1 100644 --- a/test/integration/browser/package.json +++ b/test/integration/browser/package.json @@ -7,7 +7,6 @@ "compile": "node ../../../node_modules/typescript/bin/tsc" }, "devDependencies": { - "@types/mkdirp": "^1.0.1", "@types/node": "20.x", "@types/rimraf": "^2.0.4", "@types/tmp": "0.1.0", diff --git a/test/smoke/package-lock.json b/test/smoke/package-lock.json index 4932002dbe8..af2f72d890a 100644 --- a/test/smoke/package-lock.json +++ b/test/smoke/package-lock.json @@ -9,13 +9,11 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "mkdirp": "^1.0.4", "ncp": "^2.0.0", "node-fetch": "^2.6.7", "rimraf": "3.0.2" }, "devDependencies": { - "@types/mkdirp": "^1.0.1", "@types/mocha": "^9.1.1", "@types/ncp": "2.0.1", "@types/node": "20.x", @@ -48,15 +46,6 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, - "node_modules/@types/mkdirp": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz", - "integrity": "sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -582,17 +571,6 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", diff --git a/test/smoke/package.json b/test/smoke/package.json index 1b4d142a1e7..90da59a0f53 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -11,13 +11,11 @@ "mocha": "node ../node_modules/mocha/bin/mocha" }, "dependencies": { - "mkdirp": "^1.0.4", "ncp": "^2.0.0", "node-fetch": "^2.6.7", "rimraf": "3.0.2" }, "devDependencies": { - "@types/mkdirp": "^1.0.1", "@types/mocha": "^9.1.1", "@types/ncp": "2.0.1", "@types/node": "20.x", diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 4df38db9d72..d8120ea0675 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -10,7 +10,6 @@ import * as path from 'path'; import * as os from 'os'; import * as minimist from 'minimist'; import * as rimraf from 'rimraf'; -import * as mkdirp from 'mkdirp'; import * as vscodetest from '@vscode/test-electron'; import fetch from 'node-fetch'; import { Quality, MultiLogger, Logger, ConsoleLogger, FileLogger, measureAndLog, getDevElectronPath, getBuildElectronPath, getBuildVersion } from '../../automation'; @@ -105,7 +104,7 @@ function createLogger(): Logger { // Prepare logs rot path fs.rmSync(logsRootPath, { recursive: true, force: true, maxRetries: 3 }); - mkdirp.sync(logsRootPath); + fs.mkdirSync(logsRootPath, { recursive: true }); // Always log to log file loggers.push(new FileLogger(path.join(logsRootPath, 'smoke-test-runner.log'))); @@ -123,7 +122,7 @@ const testDataPath = path.join(os.tmpdir(), 'vscsmoke'); if (fs.existsSync(testDataPath)) { rimraf.sync(testDataPath); } -mkdirp.sync(testDataPath); +fs.mkdirSync(testDataPath, { recursive: true }); process.once('exit', () => { try { rimraf.sync(testDataPath); @@ -135,7 +134,7 @@ process.once('exit', () => { const testRepoUrl = 'https://github.com/microsoft/vscode-smoketest-express'; const workspacePath = path.join(testDataPath, 'vscode-smoketest-express'); const extensionsPath = path.join(testDataPath, 'extensions-dir'); -mkdirp.sync(extensionsPath); +fs.mkdirSync(extensionsPath, { recursive: true }); function fail(errorMessage): void { logger.log(errorMessage); @@ -179,7 +178,7 @@ function parseQuality(): Quality { // if (!opts.web) { let testCodePath = opts.build; - let electronPath: string; + let electronPath: string | undefined; if (testCodePath) { electronPath = getBuildElectronPath(testCodePath); From fea0aa298b63a72a5fd50b9f647773d13ec0c33d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 08:45:20 +0200 Subject: [PATCH 24/45] Git - only calculate branch merge base on checkout (#228066) --- extensions/git/src/historyProvider.ts | 64 ++++++++++++++++++--------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 872582dae6e..5a48c70686c 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -62,7 +62,10 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec this._onDidChangeCurrentHistoryItemGroup.fire(); } + private _HEAD: Branch | undefined; private historyItemRefs: SourceControlHistoryItemRef[] = []; + private historyItemBaseRef: SourceControlHistoryItemRef | undefined; + private historyItemDecorations = new Map(); private disposables: Disposable[] = []; @@ -79,37 +82,56 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec return; } - // Get the merge base of the current history item group - const mergeBase = await this.resolveHEADMergeBase(); + let historyItemRefId = ''; + let historyItemRefName = ''; - // Handle tag, and detached commit - const currentHistoryItemGroupId = - this.repository.HEAD.name === undefined ? - this.repository.HEAD.commit : - this.repository.HEAD.type === RefType.Tag ? - `refs/tags/${this.repository.HEAD.name}` : - `refs/heads/${this.repository.HEAD.name}`; + switch (this.repository.HEAD.type) { + case RefType.Head: { + if (this.repository.HEAD.name !== undefined) { + // Branch + historyItemRefId = `refs/heads/${this.repository.HEAD.name}`; + historyItemRefName = this.repository.HEAD.name; - // Detached commit - const currentHistoryItemGroupName = - this.repository.HEAD.name ?? this.repository.HEAD.commit; + // Merge base if the branch has changed + if (this._HEAD?.name !== this.repository.HEAD.name) { + const mergeBase = await this.resolveHEADMergeBase(); + this.historyItemBaseRef = mergeBase && + (mergeBase.remote !== this.repository.HEAD.upstream?.remote || + mergeBase.name !== this.repository.HEAD.upstream?.name) ? { + id: `refs/remotes/${mergeBase.remote}/${mergeBase.name}`, + name: `${mergeBase.remote}/${mergeBase.name}`, + revision: mergeBase.commit + } : undefined; + } + } else { + // Detached commit + historyItemRefId = this.repository.HEAD.commit ?? ''; + historyItemRefName = this.repository.HEAD.commit ?? ''; + this.historyItemBaseRef = undefined; + } + break; + } + case RefType.Tag: { + // Tag + historyItemRefId = `refs/tags/${this.repository.HEAD.name}`; + historyItemRefName = this.repository.HEAD.name ?? this.repository.HEAD.commit ?? ''; + this.historyItemBaseRef = undefined; + break; + } + } + + this._HEAD = this.repository.HEAD; this.currentHistoryItemGroup = { - id: currentHistoryItemGroupId ?? '', - name: currentHistoryItemGroupName ?? '', + id: historyItemRefId, + name: historyItemRefName, revision: this.repository.HEAD.commit, remote: this.repository.HEAD.upstream ? { id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, name: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, revision: this.repository.HEAD.upstream.commit } : undefined, - base: mergeBase && - (mergeBase.remote !== this.repository.HEAD.upstream?.remote || - mergeBase.name !== this.repository.HEAD.upstream?.name) ? { - id: `refs/remotes/${mergeBase.remote}/${mergeBase.name}`, - name: `${mergeBase.remote}/${mergeBase.name}`, - revision: mergeBase.commit - } : undefined + base: this.historyItemBaseRef }; this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] currentHistoryItemGroup: ${JSON.stringify(this.currentHistoryItemGroup)}`); From 5921ef8b553ff004493010e8af64f26158b1dd1b Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 10 Sep 2024 16:03:14 +0900 Subject: [PATCH 25/45] chore: increase heap size for client watch commands (#228068) --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 70050be5067..72970d62570 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,11 @@ "kill-watch-webd": "deemon --kill npm run watch-web", "restart-watchd": "deemon --restart npm run watch", "restart-watch-webd": "deemon --restart npm run watch-web", - "watch-client": "node ./node_modules/gulp/bin/gulp.js watch-client", - "watch-client-amd": "node ./node_modules/gulp/bin/gulp.js watch-client-amd", + "watch-client": "node --max-old-space-size=8192 ./node_modules/gulp/bin/gulp.js watch-client", + "watch-client-amd": "node --max-old-space-size=8192 ./node_modules/gulp/bin/gulp.js watch-client-amd", "watch-clientd": "deemon npm run watch-client", "kill-watch-clientd": "deemon --kill npm run watch-client", - "watch-extensions": "node ./node_modules/gulp/bin/gulp.js watch-extensions watch-extension-media", + "watch-extensions": "node --max-old-space-size=8192 ./node_modules/gulp/bin/gulp.js watch-extensions watch-extension-media", "watch-extensionsd": "deemon npm run watch-extensions", "kill-watch-extensionsd": "deemon --kill npm run watch-extensions", "precommit": "node build/hygiene.js", From 4523f644ac7c76fb10bdfeb166566fccfaae8594 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 09:08:19 +0200 Subject: [PATCH 26/45] SCM - fix badge spacing in the history item hover (#228069) --- src/vs/workbench/contrib/scm/browser/media/scm.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 1e6e2a8d9e2..8c637576613 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -473,7 +473,11 @@ } .monaco-hover.history-item-hover p:last-child { - margin-bottom: 4px; + margin-bottom: 0; +} + +.monaco-hover.history-item-hover p:last-child span:not(.codicon) { + margin-bottom: 2px !important; } .monaco-hover.history-item-hover hr { @@ -485,7 +489,7 @@ margin: 4px 0; } -.monaco-hover.history-item-hover .markdown-hover .hover-contents:not(.code-hover-contents):not(.html-hover-contents) span:not(.codicon) { +.monaco-hover.history-item-hover span:not(.codicon) { margin-bottom: 0 !important; } From e96c90306bc1c10ed35fa817b178eba3fed12052 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:15:17 +0200 Subject: [PATCH 27/45] SCM - Add history item hover action (#228072) --- .../contrib/scm/browser/media/scm.css | 10 ++++++ .../contrib/scm/browser/scmHistoryViewPane.ts | 31 ++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 8c637576613..2306e5e0c50 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -493,6 +493,16 @@ margin-bottom: 0 !important; } +.monaco-hover.history-item-hover .hover-row.status-bar .action { + display: flex; + align-items: center; +} + +.monaco-hover.history-item-hover .hover-row.status-bar .action .codicon { + color: inherit; + font-size: 12px; +} + /* Graph */ .pane-header .scm-graph-view-badge-container { diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 828e8d60f33..476e480f9e5 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -60,6 +60,7 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { clamp } from '../../../../base/common/numbers.js'; import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; import { compare } from '../../../../base/common/strings.js'; +import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; type TreeElement = SCMHistoryItemViewModelTreeElement | SCMHistoryItemLoadMoreTreeElement; @@ -292,6 +293,7 @@ class HistoryItemRenderer implements ITreeRenderer this._clipboardService.writeText(historyItem.id) + }, + { + commandId: 'workbench.scm.action.copyHistoryItemMessage', + iconClass: 'codicon.codicon-copy', + label: localize('historyItemMessage', "Message"), + run: () => this._clipboardService.writeText(historyItem.message) + } + ]; + } + + private _getHoverContent(element: SCMHistoryItemViewModelTreeElement): IManagedHoverTooltipMarkdownString { const colorTheme = this._themeService.getColorTheme(); const historyItem = element.historyItemViewModel.historyItem; const markdown = new MarkdownString('', { isTrusted: true, supportThemeIcons: true }); - markdown.appendMarkdown(`$(git-commit) \`${historyItem.displayId ?? historyItem.id}\`\n\n`); + // markdown.appendMarkdown(`$(git-commit) \`${historyItem.displayId ?? historyItem.id}\`\n\n`); if (historyItem.author) { markdown.appendMarkdown(`$(account) **${historyItem.author}**`); @@ -421,7 +442,7 @@ class HistoryItemRenderer implements ITreeRenderer Date: Tue, 10 Sep 2024 11:17:57 +0200 Subject: [PATCH 28/45] SCM Graph - more improvements to refresh (#228079) --- .../contrib/scm/browser/scmHistoryViewPane.ts | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 476e480f9e5..a3c6bea513b 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -32,7 +32,7 @@ import { asCssVariable, ColorIdentifier, foreground } from '../../../../platform import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IViewPaneOptions, ViewAction, ViewPane, ViewPaneShowActions } from '../../../browser/parts/views/viewPane.js'; import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; -import { renderSCMHistoryItemGraph, historyItemGroupLocal, historyItemGroupRemote, historyItemGroupBase, toISCMHistoryItemViewModelArray, SWIMLANE_WIDTH, renderSCMHistoryGraphPlaceholder, historyItemHoverDeletionsForeground, historyItemHoverLabelForeground, historyItemHoverAdditionsForeground, historyItemHoverDefaultLabelForeground, historyItemHoverDefaultLabelBackground } from './scmHistory.js'; +import { renderSCMHistoryItemGraph, toISCMHistoryItemViewModelArray, SWIMLANE_WIDTH, renderSCMHistoryGraphPlaceholder, historyItemHoverDeletionsForeground, historyItemHoverLabelForeground, historyItemHoverAdditionsForeground, historyItemHoverDefaultLabelForeground, historyItemHoverDefaultLabelBackground } from './scmHistory.js'; import { isSCMHistoryItemLoadMoreTreeElement, isSCMHistoryItemViewModelTreeElement, isSCMRepository } from './util.js'; import { ISCMHistoryItem, ISCMHistoryItemRef, ISCMHistoryItemViewModel, ISCMHistoryProvider, SCMHistoryItemLoadMoreTreeElement, SCMHistoryItemViewModelTreeElement } from '../common/history.js'; import { HISTORY_VIEW_PANE_ID, ISCMProvider, ISCMRepository, ISCMService, ISCMViewService } from '../common/scm.js'; @@ -801,16 +801,20 @@ class SCMHistoryViewModel extends Disposable { private _getGraphColorMap(historyItemRefs: ISCMHistoryItemRef[]): Map { const repository = this.repository.get(); const historyProvider = repository?.provider.historyProvider.get(); - const currentHistoryItemGroup = historyProvider?.currentHistoryItemGroup.get(); + const historyItemRef = historyProvider?.currentHistoryItemRef.get(); + const historyItemRemoteRef = historyProvider?.currentHistoryItemRemoteRef.get(); + const historyItemBaseRef = historyProvider?.currentHistoryItemBaseRef.get(); const colorMap = new Map(); - if (currentHistoryItemGroup) { - colorMap.set(currentHistoryItemGroup.id, historyItemGroupLocal); - if (currentHistoryItemGroup.remote) { - colorMap.set(currentHistoryItemGroup.remote.id, historyItemGroupRemote); + + if (historyItemRef) { + colorMap.set(historyItemRef.id, historyItemRef.color); + + if (historyItemRemoteRef) { + colorMap.set(historyItemRemoteRef.id, historyItemRemoteRef.color); } - if (currentHistoryItemGroup.base) { - colorMap.set(currentHistoryItemGroup.base.id, historyItemGroupBase); + if (historyItemBaseRef) { + colorMap.set(historyItemBaseRef.id, historyItemBaseRef.color); } } @@ -1101,18 +1105,15 @@ export class SCMHistoryViewPane extends ViewPane { // Update context this._scmProviderCtx.set(repository.provider.contextValue); - // Commit, Checkout - const historyItemRefSignal = signalFromObservable(this, historyProvider.currentHistoryItemRef); - // Publish - const historyItemRefRemoteIdSignal = signalFromObservable(this, derived(reader => { + const historyItemRemoteRefIdSignal = signalFromObservable(this, derived(reader => { return historyProvider.currentHistoryItemRemoteRef.read(reader)?.id; })); // Fetch, Push - const historyItemRefRemoteRevisionSignal = signalFromObservable(this, derived(reader => { + const historyItemRemoteRefRevision = derived(reader => { return historyProvider.currentHistoryItemRemoteRef.read(reader)?.revision; - })); + }); // HistoryItemRefs changed store.add( @@ -1120,23 +1121,33 @@ export class SCMHistoryViewPane extends ViewPane { owner: this, createEmptyChangeSummary: () => ({ refresh: false }), handleChange(context, changeSummary) { - changeSummary.refresh = context.didChange(historyItemRefRemoteRevisionSignal) ? 'ifScrollTop' : true; + changeSummary.refresh = context.didChange(historyItemRemoteRefRevision) ? 'ifScrollTop' : true; return true; }, }, (reader, changeSummary) => { - historyItemRefSignal.read(reader); - historyItemRefRemoteIdSignal.read(reader); - historyItemRefRemoteRevisionSignal.read(reader); + historyItemRemoteRefIdSignal.read(reader); + const historyItemRefValue = historyProvider.currentHistoryItemRef.read(reader); + const historyItemRemoteRefRevisionValue = historyItemRemoteRefRevision.read(reader); + // Commit, Checkout, Publish, Pull if (changeSummary.refresh === true) { this.refresh(); return; } if (changeSummary.refresh === 'ifScrollTop') { - // Remote revision changes can occur as a result of a user action (Fetch, Push) but - // it can also occur as a result of background action (Auto Fetch). If the tree is - // scrolled to the top, we can safely refresh the tree. + // If the history item remote revision has changed, but it matches the history + // item revision, then it means that a Push operation was performed and it is + // safe to refresh the graph. + if (historyItemRefValue?.revision === historyItemRemoteRefRevisionValue) { + this.refresh(); + return; + } + + // If the history item remote revision has changed, but it does not matches the + // history item revision, then a Fetch operation was performed. This can be the + // result of a user action (Fetch) or a background action (Auto Fetch). If the + // tree is scrolled to the top, we can safely refresh the tree. if (this._tree.scrollTop === 0) { this.refresh(); return; From 62f8880800eb82f642cd7b0d7b5122e5e6574160 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:07:37 +0200 Subject: [PATCH 29/45] SCM Graph - add progress indicator for initial rendering (#228084) --- .../contrib/scm/browser/scmHistoryViewPane.ts | 155 +++++++++--------- 1 file changed, 80 insertions(+), 75 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index a3c6bea513b..52a30c7f6ce 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -326,8 +326,8 @@ class HistoryItemRenderer implements ITreeRenderer { - if (visible) { - this._treeViewModel = this.instantiationService.createInstance(SCMHistoryViewModel); - this._visibilityDisposables.add(this._treeViewModel); + if (!visible) { + this._visibilityDisposables.clear(); + return; + } + // Create view model + this._treeViewModel = this.instantiationService.createInstance(SCMHistoryViewModel); + this._visibilityDisposables.add(this._treeViewModel); + + // Initial rendering + await this._progressService.withProgress({ location: this.id }, async () => { const firstRepositoryInitialized = derived(this, reader => { const repository = this._treeViewModel.repository.read(reader); const historyProvider = repository?.provider.historyProvider.read(reader); - const currentHistoryItemGroup = historyProvider?.currentHistoryItemGroup.read(reader); + const historyItemRef = historyProvider?.currentHistoryItemRef.read(reader); - return currentHistoryItemGroup !== undefined ? repository : undefined; + return historyItemRef !== undefined ? true : undefined; }); // Wait for first repository to be initialized await waitForState(firstRepositoryInitialized); // Set tree input - this._treeOperationSequencer.queue(async () => { + await this._treeOperationSequencer.queue(async () => { await this._tree.setInput(this._treeViewModel); this._tree.scrollTop = 0; }); + }); - // Repository change - let isFirstRun = true; - this._visibilityDisposables.add(autorunWithStore((reader, store) => { - const repository = this._treeViewModel.repository.read(reader); - const historyProvider = repository?.provider.historyProvider.read(reader); - if (!repository || !historyProvider) { - return; - } + // Repository change + let isFirstRun = true; + this._visibilityDisposables.add(autorunWithStore((reader, store) => { + const repository = this._treeViewModel.repository.read(reader); + const historyProvider = repository?.provider.historyProvider.read(reader); + if (!repository || !historyProvider) { + return; + } - // Update context - this._scmProviderCtx.set(repository.provider.contextValue); + // Update context + this._scmProviderCtx.set(repository.provider.contextValue); - // Publish - const historyItemRemoteRefIdSignal = signalFromObservable(this, derived(reader => { - return historyProvider.currentHistoryItemRemoteRef.read(reader)?.id; - })); + // Publish + const historyItemRemoteRefIdSignal = signalFromObservable(this, derived(reader => { + return historyProvider.currentHistoryItemRemoteRef.read(reader)?.id; + })); - // Fetch, Push - const historyItemRemoteRefRevision = derived(reader => { - return historyProvider.currentHistoryItemRemoteRef.read(reader)?.revision; - }); + // Fetch, Push + const historyItemRemoteRefRevision = derived(reader => { + return historyProvider.currentHistoryItemRemoteRef.read(reader)?.revision; + }); - // HistoryItemRefs changed - store.add( - autorunWithStoreHandleChanges<{ refresh: boolean | 'ifScrollTop' }>({ - owner: this, - createEmptyChangeSummary: () => ({ refresh: false }), - handleChange(context, changeSummary) { - changeSummary.refresh = context.didChange(historyItemRemoteRefRevision) ? 'ifScrollTop' : true; - return true; - }, - }, (reader, changeSummary) => { - historyItemRemoteRefIdSignal.read(reader); - const historyItemRefValue = historyProvider.currentHistoryItemRef.read(reader); - const historyItemRemoteRefRevisionValue = historyItemRemoteRefRevision.read(reader); + // HistoryItemRefs changed + store.add( + autorunWithStoreHandleChanges<{ refresh: boolean | 'ifScrollTop' }>({ + owner: this, + createEmptyChangeSummary: () => ({ refresh: false }), + handleChange(context, changeSummary) { + changeSummary.refresh = context.didChange(historyItemRemoteRefRevision) ? 'ifScrollTop' : true; + return true; + }, + }, (reader, changeSummary) => { + historyItemRemoteRefIdSignal.read(reader); + const historyItemRefValue = historyProvider.currentHistoryItemRef.read(reader); + const historyItemRemoteRefRevisionValue = historyItemRemoteRefRevision.read(reader); - // Commit, Checkout, Publish, Pull - if (changeSummary.refresh === true) { + // Commit, Checkout, Publish, Pull + if (changeSummary.refresh === true) { + this.refresh(); + return; + } + + if (changeSummary.refresh === 'ifScrollTop') { + // If the history item remote revision has changed, but it matches the history + // item revision, then it means that a Push operation was performed and it is + // safe to refresh the graph. + if (historyItemRefValue?.revision === historyItemRemoteRefRevisionValue) { this.refresh(); return; } - if (changeSummary.refresh === 'ifScrollTop') { - // If the history item remote revision has changed, but it matches the history - // item revision, then it means that a Push operation was performed and it is - // safe to refresh the graph. - if (historyItemRefValue?.revision === historyItemRemoteRefRevisionValue) { - this.refresh(); - return; - } - - // If the history item remote revision has changed, but it does not matches the - // history item revision, then a Fetch operation was performed. This can be the - // result of a user action (Fetch) or a background action (Auto Fetch). If the - // tree is scrolled to the top, we can safely refresh the tree. - if (this._tree.scrollTop === 0) { - this.refresh(); - return; - } - - // Show the "Outdated" badge on the view - this._repositoryOutdated.set(true, undefined); + // If the history item remote revision has changed, but it does not matches the + // history item revision, then a Fetch operation was performed. This can be the + // result of a user action (Fetch) or a background action (Auto Fetch). If the + // tree is scrolled to the top, we can safely refresh the tree. + if (this._tree.scrollTop === 0) { + this.refresh(); + return; } - })); - // HistoryItemRefs filter changed - store.add(runOnChange(this._treeViewModel.historyItemsFilter, () => { - this.refresh(); + // Show the "Outdated" badge on the view + this._repositoryOutdated.set(true, undefined); + } })); - // We skip refreshing the graph on the first execution of the autorun - // since the graph for the first repository is rendered when the tree - // input is set. - if (!isFirstRun) { - this.refresh(); - } - isFirstRun = false; + // HistoryItemRefs filter changed + store.add(runOnChange(this._treeViewModel.historyItemsFilter, () => { + this.refresh(); })); - } else { - this._visibilityDisposables.clear(); - } + + // We skip refreshing the graph on the first execution of the autorun + // since the graph for the first repository is rendered when the tree + // input is set. + if (!isFirstRun) { + this.refresh(); + } + isFirstRun = false; + })); }); } From c2f7ee51b3c2fc25d94a909533d72b5f5eb3b1ff Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 10 Sep 2024 12:11:22 +0200 Subject: [PATCH 30/45] chat input completions must use more honest filterText (#228086) --- .../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 60a91948272..231e5d2bff3 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -383,7 +383,7 @@ class BuiltinDynamicCompletions extends Disposable { return { label: { label: basename, description: this.labelService.getUriLabel(resource) }, - filterText: info.varWord?.word, + filterText: `${chatVariableLeader}${basename}`, insertText, range: info, kind: CompletionItemKind.File, From b1d3ce462b164ffe0115b616fbcf702dddd16744 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:33:18 +0200 Subject: [PATCH 31/45] SCM Graph - fix action ids to be consistent with other scm workbench actions (#228091) --- .../contrib/scm/browser/scmHistoryViewPane.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 52a30c7f6ce..0b9c0b6bcbf 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -62,6 +62,9 @@ import { observableConfigValue } from '../../../../platform/observable/common/pl import { compare } from '../../../../base/common/strings.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; +const PICK_REPOSITORY_ACTION_ID = 'workbench.scm.action.graph.pickRepository'; +const PICK_HISTORY_ITEM_REFS_ACTION_ID = 'workbench.scm.action.graph.pickHistoryItemRefs'; + type TreeElement = SCMHistoryItemViewModelTreeElement | SCMHistoryItemLoadMoreTreeElement; class SCMRepositoryActionViewItem extends ActionViewItem { @@ -144,7 +147,7 @@ class SCMHistoryItemRefsActionViewItem extends ActionViewItem { registerAction2(class extends ViewAction { constructor() { super({ - id: 'workbench.scm.graph.action.pickRepository', + id: PICK_REPOSITORY_ACTION_ID, title: '', viewId: HISTORY_VIEW_PANE_ID, f1: false, @@ -165,7 +168,7 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: 'workbench.scm.graph.action.pickHistoryItemRefs', + id: PICK_HISTORY_ITEM_REFS_ACTION_ID, title: '', icon: Codicon.gitBranch, viewId: HISTORY_VIEW_PANE_ID, @@ -187,7 +190,7 @@ registerAction2(class extends ViewAction { registerAction2(class extends ViewAction { constructor() { super({ - id: 'workbench.scm.action.refreshGraph', + id: 'workbench.scm.action.graph.refresh', title: localize('refreshGraph', "Refresh"), viewId: HISTORY_VIEW_PANE_ID, f1: false, @@ -208,7 +211,7 @@ registerAction2(class extends ViewAction { registerAction2(class extends Action2 { constructor() { super({ - id: 'workbench.scm.action.scm.viewChanges', + id: 'workbench.scm.action.graph.viewChanges', title: localize('viewChanges', "View Changes"), f1: false, menu: [ @@ -371,13 +374,13 @@ class HistoryItemRenderer implements ITreeRenderer this._clipboardService.writeText(historyItem.id) }, { - commandId: 'workbench.scm.action.copyHistoryItemMessage', + commandId: 'workbench.scm.action.graph.copyHistoryItemMessage', iconClass: 'codicon.codicon-copy', label: localize('historyItemMessage', "Message"), run: () => this._clipboardService.writeText(historyItem.message) @@ -1196,12 +1199,12 @@ export class SCMHistoryViewPane extends ViewPane { } override getActionViewItem(action: IAction, options?: IDropdownMenuActionViewItemOptions): IActionViewItem | undefined { - if (action.id === 'workbench.scm.graph.action.pickRepository') { + if (action.id === PICK_REPOSITORY_ACTION_ID) { const repository = this._treeViewModel?.repository.get(); if (repository) { return new SCMRepositoryActionViewItem(repository, action, options); } - } else if (action.id === 'workbench.scm.graph.action.pickHistoryItemRefs') { + } else if (action.id === PICK_HISTORY_ITEM_REFS_ACTION_ID) { const repository = this._treeViewModel?.repository.get(); const historyItemsFilter = this._treeViewModel?.historyItemsFilter.get(); if (repository && historyItemsFilter) { From 3f0e1da18456e116e612649854d626741a03962e Mon Sep 17 00:00:00 2001 From: BABA <38986298+BABA983@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:25:11 +0800 Subject: [PATCH 32/45] Register fold import action (#227216) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Register fold import action * Get regions from foldingModel * Rename toggle import fold action * 💄 --------- Co-authored-by: Martin Aeschlimann --- .../editor/contrib/folding/browser/folding.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index 2380c725389..ee657d44e14 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -1231,6 +1231,35 @@ class RemoveFoldRangeFromSelectionAction extends FoldingAction { } +class ToggleImportFoldAction extends FoldingAction { + + constructor() { + super({ + id: 'editor.toggleImportFold', + label: nls.localize('toggleImportFold.label', "Toggle Import Fold"), + alias: 'Toggle Import Fold', + precondition: CONTEXT_FOLDING_ENABLED, + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + weight: KeybindingWeight.EditorContrib + } + }); + } + + async invoke(foldingController: FoldingController, foldingModel: FoldingModel): Promise { + const regionsToToggle: FoldingRegion[] = []; + const regions = foldingModel.regions; + for (let i = regions.length - 1; i >= 0; i--) { + if (regions.getType(i) === FoldingRangeKind.Imports.value) { + regionsToToggle.push(regions.toRegion(i)); + } + } + foldingModel.toggleCollapseState(regionsToToggle); + foldingController.triggerFoldingModelChanged(); + } +} + + registerEditorContribution(FoldingController.ID, FoldingController, EditorContributionInstantiation.Eager); // eager because it uses `saveViewState`/`restoreViewState` registerEditorAction(UnfoldAction); registerEditorAction(UnFoldRecursivelyAction); @@ -1250,6 +1279,7 @@ registerEditorAction(GotoPreviousFoldAction); registerEditorAction(GotoNextFoldAction); registerEditorAction(FoldRangeFromSelectionAction); registerEditorAction(RemoveFoldRangeFromSelectionAction); +registerEditorAction(ToggleImportFoldAction); for (let i = 1; i <= 7; i++) { registerInstantiatedEditorAction( From c2c032e0da150de8222a0e7f53e3bb6f2891eb0f Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:46:12 +0200 Subject: [PATCH 33/45] =?UTF-8?q?SCM=20-=20=F0=9F=92=84=20remove=20SourceC?= =?UTF-8?q?ontrolHistoryItemGroup=20from=20the=20API=20proposal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/commands.ts | 4 +- extensions/git/src/decorationProvider.ts | 13 +-- extensions/git/src/historyProvider.ts | 68 ++++++++------ src/vs/workbench/api/browser/mainThreadSCM.ts | 88 +++++++------------ .../workbench/api/common/extHost.protocol.ts | 10 +-- src/vs/workbench/api/common/extHostSCM.ts | 14 ++- .../workbench/contrib/scm/browser/activity.ts | 4 +- .../contrib/scm/browser/scmHistory.ts | 12 +-- .../contrib/scm/browser/scmHistoryViewPane.ts | 28 +++--- .../contrib/scm/browser/workingSet.ts | 18 ++-- .../workbench/contrib/scm/common/history.ts | 16 +--- .../scm/test/browser/scmHistory.test.ts | 36 ++++---- .../vscode.proposed.scmHistoryProvider.d.ts | 18 ++-- 13 files changed, 149 insertions(+), 180 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index d80a203b5c3..2b4e7d2dfaa 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3059,7 +3059,7 @@ export class CommandCenter { @command('git.fetchRef', { repository: true }) async fetchRef(repository: Repository, ref?: string): Promise { - ref = ref ?? repository?.historyProvider.currentHistoryItemGroup?.remote?.id; + ref = ref ?? repository?.historyProvider.currentHistoryItemRemoteRef?.id; if (!repository || !ref) { return; } @@ -3132,7 +3132,7 @@ export class CommandCenter { @command('git.pullRef', { repository: true }) async pullRef(repository: Repository, ref?: string): Promise { - ref = ref ?? repository?.historyProvider.currentHistoryItemGroup?.remote?.id; + ref = ref ?? repository?.historyProvider.currentHistoryItemRemoteRef?.id; if (!repository || !ref) { return; } diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 6de2cfae31d..f1c675ceff1 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -164,11 +164,11 @@ class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider constructor(private readonly repository: Repository) { this.disposables.push( window.registerFileDecorationProvider(this), - runAndSubscribeEvent(repository.historyProvider.onDidChangeCurrentHistoryItemGroup, () => this.onDidChangeCurrentHistoryItemGroup()) + runAndSubscribeEvent(repository.historyProvider.onDidChangeCurrentHistoryItemRefs, () => this.onDidChangeCurrentHistoryItemRefs()) ); } - private async onDidChangeCurrentHistoryItemGroup(): Promise { + private async onDidChangeCurrentHistoryItemRefs(): Promise { const newDecorations = new Map(); await this.collectIncomingChangesFileDecorations(newDecorations); const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()])); @@ -218,18 +218,19 @@ class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider private async getIncomingChanges(): Promise { try { const historyProvider = this.repository.historyProvider; - const currentHistoryItemGroup = historyProvider.currentHistoryItemGroup; + const currentHistoryItemRef = historyProvider.currentHistoryItemRef; + const currentHistoryItemRemoteRef = historyProvider.currentHistoryItemRemoteRef; - if (!currentHistoryItemGroup?.remote) { + if (!currentHistoryItemRef || !currentHistoryItemRemoteRef) { return []; } - const ancestor = await historyProvider.resolveHistoryItemRefsCommonAncestor([currentHistoryItemGroup.id, currentHistoryItemGroup.remote.id]); + const ancestor = await historyProvider.resolveHistoryItemRefsCommonAncestor([currentHistoryItemRef.id, currentHistoryItemRemoteRef.id]); if (!ancestor) { return []; } - const changes = await this.repository.diffBetween(ancestor, currentHistoryItemGroup.remote.id); + const changes = await this.repository.diffBetween(ancestor, currentHistoryItemRemoteRef.id); return changes; } catch (err) { return []; diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 5a48c70686c..3d115d8c116 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryItemGroup, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryItemRef, l10n, SourceControlHistoryItemRefsChangeEvent } from 'vscode'; +import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryItemRef, l10n, SourceControlHistoryItemRefsChangeEvent } from 'vscode'; import { Repository, Resource } from './repository'; import { IDisposable, deltaHistoryItemRefs, dispose } from './util'; import { toGitUri } from './uri'; @@ -45,22 +45,23 @@ function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { } export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable { - - private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter(); - readonly onDidChangeCurrentHistoryItemGroup: Event = this._onDidChangeCurrentHistoryItemGroup.event; - - private readonly _onDidChangeHistoryItemRefs = new EventEmitter(); - readonly onDidChangeHistoryItemRefs: Event = this._onDidChangeHistoryItemRefs.event; - private readonly _onDidChangeDecorations = new EventEmitter(); readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; - private _currentHistoryItemGroup: SourceControlHistoryItemGroup | undefined; - get currentHistoryItemGroup(): SourceControlHistoryItemGroup | undefined { return this._currentHistoryItemGroup; } - set currentHistoryItemGroup(value: SourceControlHistoryItemGroup | undefined) { - this._currentHistoryItemGroup = value; - this._onDidChangeCurrentHistoryItemGroup.fire(); - } + private _currentHistoryItemRef: SourceControlHistoryItemRef | undefined; + get currentHistoryItemRef(): SourceControlHistoryItemRef | undefined { return this._currentHistoryItemRef; } + + private _currentHistoryItemRemoteRef: SourceControlHistoryItemRef | undefined; + get currentHistoryItemRemoteRef(): SourceControlHistoryItemRef | undefined { return this._currentHistoryItemRemoteRef; } + + private _currentHistoryItemBaseRef: SourceControlHistoryItemRef | undefined; + get currentHistoryItemBaseRef(): SourceControlHistoryItemRef | undefined { return this._currentHistoryItemBaseRef; } + + private readonly _onDidChangeCurrentHistoryItemRefs = new EventEmitter(); + readonly onDidChangeCurrentHistoryItemRefs: Event = this._onDidChangeCurrentHistoryItemRefs.event; + + private readonly _onDidChangeHistoryItemRefs = new EventEmitter(); + readonly onDidChangeHistoryItemRefs: Event = this._onDidChangeHistoryItemRefs.event; private _HEAD: Branch | undefined; private historyItemRefs: SourceControlHistoryItemRef[] = []; @@ -78,7 +79,9 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec private async onDidRunGitStatus(): Promise { if (!this.repository.HEAD) { this.logger.trace('[GitHistoryProvider][onDidRunGitStatus] repository.HEAD is undefined'); - this.currentHistoryItemGroup = undefined; + this._currentHistoryItemRef = this._currentHistoryItemRemoteRef = this._currentHistoryItemBaseRef = undefined; + this._onDidChangeCurrentHistoryItemRefs.fire(); + return; } @@ -122,19 +125,26 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec this._HEAD = this.repository.HEAD; - this.currentHistoryItemGroup = { + this._currentHistoryItemRef = { id: historyItemRefId, name: historyItemRefName, revision: this.repository.HEAD.commit, - remote: this.repository.HEAD.upstream ? { - id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, - name: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, - revision: this.repository.HEAD.upstream.commit - } : undefined, - base: this.historyItemBaseRef + icon: new ThemeIcon('target'), }; - this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] currentHistoryItemGroup: ${JSON.stringify(this.currentHistoryItemGroup)}`); + this._currentHistoryItemRemoteRef = this.repository.HEAD.upstream ? { + id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, + name: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, + revision: this.repository.HEAD.upstream.commit, + icon: new ThemeIcon('cloud') + } : undefined; + + this._currentHistoryItemBaseRef = this.historyItemBaseRef; + + this._onDidChangeCurrentHistoryItemRefs.fire(); + this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] currentHistoryItemRef: ${JSON.stringify(this._currentHistoryItemRef)}`); + this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] currentHistoryItemRemoteRef: ${JSON.stringify(this._currentHistoryItemRemoteRef)}`); + this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] currentHistoryItemBaseRef: ${JSON.stringify(this._currentHistoryItemBaseRef)}`); // Refs (alphabetically) const refs = await this.repository.getRefs({ sort: 'alphabetically' }); @@ -177,7 +187,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } async provideHistoryItems(options: SourceControlHistoryOptions): Promise { - if (!this.currentHistoryItemGroup || !options.historyItemRefs) { + if (!this.currentHistoryItemRef || !options.historyItemRefs) { return []; } @@ -265,16 +275,16 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec if (historyItemRefs.length === 0) { // TODO@lszomoru - log return undefined; - } else if (historyItemRefs.length === 1 && historyItemRefs[0] === this.currentHistoryItemGroup?.id) { + } else if (historyItemRefs.length === 1 && historyItemRefs[0] === this.currentHistoryItemRemoteRef?.id) { // Remote - if (this.currentHistoryItemGroup.remote) { - const ancestor = await this.repository.getMergeBase(historyItemRefs[0], this.currentHistoryItemGroup.remote.id); + if (this.currentHistoryItemRemoteRef) { + const ancestor = await this.repository.getMergeBase(historyItemRefs[0], this.currentHistoryItemRemoteRef.id); return ancestor; } // Base - if (this.currentHistoryItemGroup.base) { - const ancestor = await this.repository.getMergeBase(historyItemRefs[0], this.currentHistoryItemGroup.base.id); + if (this.currentHistoryItemBaseRef) { + const ancestor = await this.repository.getMergeBase(historyItemRefs[0], this.currentHistoryItemBaseRef.id); return ancestor; } diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index d713066de80..bbb27f6f9b5 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -6,10 +6,10 @@ import { Barrier } from '../../../base/common/async.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { Event, Emitter } from '../../../base/common/event.js'; -import { derivedOpts, IObservable, observableValue, observableValueOpts } from '../../../base/common/observable.js'; +import { IObservable, observableValue, observableValueOpts, transaction } from '../../../base/common/observable.js'; import { IDisposable, DisposableStore, combinedDisposable, dispose, Disposable } from '../../../base/common/lifecycle.js'; import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType, ISCMActionButtonDescriptor } from '../../contrib/scm/common/scm.js'; -import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemGroupDto, SCMHistoryItemDto, SCMHistoryItemRefsChangeEventDto } from '../common/extHost.protocol.js'; +import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemDto, SCMHistoryItemRefsChangeEventDto, SCMHistoryItemRefDto } from '../common/extHost.protocol.js'; import { Command } from '../../../editor/common/languages.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { CancellationToken } from '../../../base/common/cancellation.js'; @@ -17,7 +17,7 @@ import { MarshalledId } from '../../../base/common/marshallingIds.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { IMarkdownString } from '../../../base/common/htmlContent.js'; import { IQuickDiffService, QuickDiffProvider } from '../../contrib/scm/common/quickDiff.js'; -import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup, ISCMHistoryItemRef, ISCMHistoryItemRefsChangeEvent, ISCMHistoryOptions, ISCMHistoryProvider } from '../../contrib/scm/common/history.js'; +import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemRef, ISCMHistoryItemRefsChangeEvent, ISCMHistoryOptions, ISCMHistoryProvider } from '../../contrib/scm/common/history.js'; import { ResourceTree } from '../../../base/common/resourceTree.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceContextService } from '../../../platform/workspace/common/workspace.js'; @@ -28,8 +28,8 @@ import { ITextModelContentProvider, ITextModelService } from '../../../editor/co import { Schemas } from '../../../base/common/network.js'; import { ITextModel } from '../../../editor/common/model.js'; import { structuralEquals } from '../../../base/common/equals.js'; -import { Codicon } from '../../../base/common/codicons.js'; -import { historyItemGroupBase, historyItemGroupLocal, historyItemGroupRemote } from '../../contrib/scm/browser/scmHistory.js'; +import { historyItemBaseRefColor, historyItemRefColor, historyItemRemoteRefColor } from '../../contrib/scm/browser/scmHistory.js'; +import { ColorIdentifier } from '../../../platform/theme/common/colorUtils.js'; function getIconFromIconDto(iconDto?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon): URI | { light: URI; dark: URI } | ThemeIcon | undefined { if (iconDto === undefined) { @@ -56,6 +56,10 @@ function toISCMHistoryItem(historyItemDto: SCMHistoryItemDto): ISCMHistoryItem { return { ...historyItemDto, subject, references }; } +function toISCMHistoryItemRef(historyItemRefDto?: SCMHistoryItemRefDto, color?: ColorIdentifier): ISCMHistoryItemRef | undefined { + return historyItemRefDto ? { ...historyItemRefDto, icon: getIconFromIconDto(historyItemRefDto.icon), color: color } : undefined; +} + class SCMInputBoxContentProvider extends Disposable implements ITextModelContentProvider { constructor( textModelService: ITextModelService, @@ -168,55 +172,23 @@ class MainThreadSCMResource implements ISCMResource { } class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { - private readonly _currentHistoryItemGroup = observableValueOpts({ - owner: this, equalsFn: structuralEquals + private readonly _historyItemRef = observableValueOpts({ + owner: this, + equalsFn: structuralEquals }, undefined); - get currentHistoryItemGroup() { return this._currentHistoryItemGroup; } + get historyItemRef(): IObservable { return this._historyItemRef; } - readonly currentHistoryItemRef = derivedOpts({ + private readonly _historyItemRemoteRef = observableValueOpts({ owner: this, equalsFn: structuralEquals - }, reader => { - const currentHistoryItemGroup = this._currentHistoryItemGroup.read(reader); + }, undefined); + get historyItemRemoteRef(): IObservable { return this._historyItemRemoteRef; } - return currentHistoryItemGroup ? { - id: currentHistoryItemGroup.id ?? '', - name: currentHistoryItemGroup.name, - revision: currentHistoryItemGroup.revision, - color: historyItemGroupLocal, - icon: Codicon.target, - } : undefined; - }); - - readonly currentHistoryItemRemoteRef = derivedOpts({ + private readonly _historyItemBaseRef = observableValueOpts({ owner: this, equalsFn: structuralEquals - }, reader => { - const currentHistoryItemGroup = this._currentHistoryItemGroup.read(reader); - - return currentHistoryItemGroup?.remote ? { - id: currentHistoryItemGroup.remote.id ?? '', - name: currentHistoryItemGroup.remote.name, - revision: currentHistoryItemGroup.remote.revision, - color: historyItemGroupRemote, - icon: Codicon.cloud, - } : undefined; - }); - - readonly currentHistoryItemBaseRef = derivedOpts({ - owner: this, - equalsFn: structuralEquals - }, reader => { - const currentHistoryItemGroup = this._currentHistoryItemGroup.read(reader); - - return currentHistoryItemGroup?.base ? { - id: currentHistoryItemGroup.base.id ?? '', - name: currentHistoryItemGroup.base.name, - revision: currentHistoryItemGroup.base.revision, - color: historyItemGroupBase, - icon: Codicon.cloud, - } : undefined; - }); + }, undefined); + get historyItemBaseRef(): IObservable { return this._historyItemBaseRef; } private readonly _historyItemRefChanges = observableValue(this, { added: [], modified: [], removed: [] }); get historyItemRefChanges(): IObservable { return this._historyItemRefChanges; } @@ -247,14 +219,18 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider { })); } - $onDidChangeCurrentHistoryItemGroup(historyItemGroup: ISCMHistoryItemGroup | undefined): void { - this._currentHistoryItemGroup.set(historyItemGroup, undefined); + $onDidChangeCurrentHistoryItemRefs(historyItemRef?: SCMHistoryItemRefDto, historyItemRemoteRef?: SCMHistoryItemRefDto, historyItemBaseRef?: SCMHistoryItemRefDto): void { + transaction(tx => { + this._historyItemRef.set(toISCMHistoryItemRef(historyItemRef, historyItemRefColor), tx); + this._historyItemRemoteRef.set(toISCMHistoryItemRef(historyItemRemoteRef, historyItemRemoteRefColor), tx); + this._historyItemBaseRef.set(toISCMHistoryItemRef(historyItemBaseRef, historyItemBaseRefColor), tx); + }); } $onDidChangeHistoryItemRefs(historyItemRefs: SCMHistoryItemRefsChangeEventDto): void { - const added = historyItemRefs.added.map(ref => ({ ...ref, icon: getIconFromIconDto(ref.icon) })); - const modified = historyItemRefs.modified.map(ref => ({ ...ref, icon: getIconFromIconDto(ref.icon) })); - const removed = historyItemRefs.removed.map(ref => ({ ...ref, icon: getIconFromIconDto(ref.icon) })); + const added = historyItemRefs.added.map(ref => toISCMHistoryItemRef(ref)!); + const modified = historyItemRefs.modified.map(ref => toISCMHistoryItemRef(ref)!); + const removed = historyItemRefs.removed.map(ref => toISCMHistoryItemRef(ref)!); this._historyItemRefChanges.set({ added, modified, removed }, undefined); } @@ -487,12 +463,12 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { return result && URI.revive(result); } - $onDidChangeHistoryProviderCurrentHistoryItemGroup(currentHistoryItemGroup?: SCMHistoryItemGroupDto): void { + $onDidChangeHistoryProviderCurrentHistoryItemRefs(historyItemRef?: SCMHistoryItemRefDto, historyItemRemoteRef?: SCMHistoryItemRefDto, historyItemBaseRef?: SCMHistoryItemRefDto): void { if (!this.historyProvider.get()) { return; } - this._historyProvider.get()?.$onDidChangeCurrentHistoryItemGroup(currentHistoryItemGroup); + this._historyProvider.get()?.$onDidChangeCurrentHistoryItemRefs(historyItemRef, historyItemRemoteRef, historyItemBaseRef); } $onDidChangeHistoryProviderHistoryItemRefs(historyItemRefs: SCMHistoryItemRefsChangeEventDto): void { @@ -736,7 +712,7 @@ export class MainThreadSCM implements MainThreadSCMShape { } } - async $onDidChangeHistoryProviderCurrentHistoryItemGroup(sourceControlHandle: number, historyItemGroup: SCMHistoryItemGroupDto | undefined): Promise { + async $onDidChangeHistoryProviderCurrentHistoryItemRefs(sourceControlHandle: number, historyItemRef?: SCMHistoryItemRefDto, historyItemRemoteRef?: SCMHistoryItemRefDto, historyItemBaseRef?: SCMHistoryItemRefDto): Promise { await this._repositoryBarriers.get(sourceControlHandle)?.wait(); const repository = this._repositories.get(sourceControlHandle); @@ -745,7 +721,7 @@ export class MainThreadSCM implements MainThreadSCMShape { } const provider = repository.provider as MainThreadSCMProvider; - provider.$onDidChangeHistoryProviderCurrentHistoryItemGroup(historyItemGroup); + provider.$onDidChangeHistoryProviderCurrentHistoryItemRefs(historyItemRef, historyItemRemoteRef, historyItemBaseRef); } async $onDidChangeHistoryProviderHistoryItemRefs(sourceControlHandle: number, historyItemRefs: SCMHistoryItemRefsChangeEventDto): Promise { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 2055d997aa0..274e29330d6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1542,14 +1542,6 @@ export type SCMRawResourceSplices = [ SCMRawResourceSplice[] ]; -export interface SCMHistoryItemGroupDto { - readonly id: string; - readonly name: string; - readonly revision?: string; - readonly base?: Omit, 'remote'>; - readonly remote?: Omit, 'remote'>; -} - export interface SCMHistoryItemRefDto { readonly id: string; readonly name: string; @@ -1606,7 +1598,7 @@ export interface MainThreadSCMShape extends IDisposable { $showValidationMessage(sourceControlHandle: number, message: string | IMarkdownString, type: InputValidationType): Promise; $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): Promise; - $onDidChangeHistoryProviderCurrentHistoryItemGroup(sourceControlHandle: number, historyItemGroup: SCMHistoryItemGroupDto | undefined): Promise; + $onDidChangeHistoryProviderCurrentHistoryItemRefs(sourceControlHandle: number, historyItemRef?: SCMHistoryItemRefDto, historyItemRemoteRef?: SCMHistoryItemRefDto, historyItemBaseRef?: SCMHistoryItemRefDto): Promise; $onDidChangeHistoryProviderHistoryItemRefs(sourceControlHandle: number, historyItemRefs: SCMHistoryItemRefsChangeEventDto): Promise; } diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 1f15f83d60a..766f5cc9d5f 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -79,6 +79,10 @@ function toSCMHistoryItemDto(historyItem: vscode.SourceControlHistoryItem): SCMH return { ...historyItem, references }; } +function toSCMHistoryItemRefDto(historyItemRef?: vscode.SourceControlHistoryItemRef): SCMHistoryItemRefDto | undefined { + return historyItemRef ? { ...historyItemRef, icon: getHistoryItemIconDto(historyItemRef.icon) } : undefined; +} + function compareResourceThemableDecorations(a: vscode.SourceControlResourceThemableDecorations, b: vscode.SourceControlResourceThemableDecorations): number { if (!a.iconPath && !b.iconPath) { return 0; @@ -577,7 +581,6 @@ class ExtHostSourceControl implements vscode.SourceControl { private _historyProvider: vscode.SourceControlHistoryProvider | undefined; private readonly _historyProviderDisposable = new MutableDisposable(); - private _historyProviderCurrentHistoryItemGroup: vscode.SourceControlHistoryItemGroup | undefined; get historyProvider(): vscode.SourceControlHistoryProvider | undefined { checkProposedApiEnabled(this._extension, 'scmHistoryProvider'); @@ -593,9 +596,12 @@ class ExtHostSourceControl implements vscode.SourceControl { this.#proxy.$updateSourceControl(this.handle, { hasHistoryProvider: !!historyProvider }); if (historyProvider) { - this._historyProviderDisposable.value.add(historyProvider.onDidChangeCurrentHistoryItemGroup(() => { - this._historyProviderCurrentHistoryItemGroup = historyProvider?.currentHistoryItemGroup; - this.#proxy.$onDidChangeHistoryProviderCurrentHistoryItemGroup(this.handle, this._historyProviderCurrentHistoryItemGroup); + this._historyProviderDisposable.value.add(historyProvider.onDidChangeCurrentHistoryItemRefs(() => { + const historyItemRef = toSCMHistoryItemRefDto(historyProvider?.currentHistoryItemRef); + const historyItemRemoteRef = toSCMHistoryItemRefDto(historyProvider?.currentHistoryItemRemoteRef); + const historyItemBaseRef = toSCMHistoryItemRefDto(historyProvider?.currentHistoryItemBaseRef); + + this.#proxy.$onDidChangeHistoryProviderCurrentHistoryItemRefs(this.handle, historyItemRef, historyItemRemoteRef, historyItemBaseRef); })); this._historyProviderDisposable.value.add(historyProvider.onDidChangeHistoryItemRefs((e) => { if (e.added.length === 0 && e.modified.length === 0 && e.removed.length === 0) { diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index faca874aa54..181ccffb1eb 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -39,9 +39,9 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe private readonly _activeRepositoryCurrentHistoryItemGroupName = derived(reader => { const repository = this.scmViewService.activeRepository.read(reader); const historyProvider = repository?.provider.historyProvider.read(reader); - const currentHistoryItemGroup = historyProvider?.currentHistoryItemGroup.read(reader); + const historyItemRef = historyProvider?.historyItemRef.read(reader); - return currentHistoryItemGroup?.name; + return historyItemRef?.name; }); private readonly _countBadgeRepositories = derived(this, reader => { diff --git a/src/vs/workbench/contrib/scm/browser/scmHistory.ts b/src/vs/workbench/contrib/scm/browser/scmHistory.ts index 08e24fe1870..0343b271fc5 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistory.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistory.ts @@ -19,11 +19,11 @@ const CIRCLE_RADIUS = 4; const SWIMLANE_CURVE_RADIUS = 5; /** - * History graph colors (local, remote, base) + * History item reference colors (local, remote, base) */ -export const historyItemGroupLocal = registerColor('scmGraph.historyItemGroupLocal', chartsBlue, localize('scmGraphHistoryItemGroupLocal', "Local history item group color.")); -export const historyItemGroupRemote = registerColor('scmGraph.historyItemGroupRemote', chartsPurple, localize('scmGraphHistoryItemGroupRemote', "Remote history item group color.")); -export const historyItemGroupBase = registerColor('scmGraph.historyItemGroupBase', chartsOrange, localize('scmGraphHistoryItemGroupBase', "Base history item group color.")); +export const historyItemRefColor = registerColor('scmGraph.historyItemRefColor', chartsBlue, localize('scmGraphHistoryItemRefColor', "History item reference color.")); +export const historyItemRemoteRefColor = registerColor('scmGraph.historyItemRemoteRefColor', chartsPurple, localize('scmGraphHistoryItemRemoteRefColor', "History item remote reference color.")); +export const historyItemBaseRefColor = registerColor('scmGraph.historyItemBaseRefColor', chartsOrange, localize('scmGraphHistoryItemBaseRefColor', "History item base reference color.")); /** * History item hover color @@ -107,7 +107,7 @@ export function renderSCMHistoryItemGraph(historyItemViewModel: ISCMHistoryItemV // Circle color - use the output swimlane color if present, otherwise the input swimlane color const circleColor = circleIndex < outputSwimlanes.length ? outputSwimlanes[circleIndex].color : - circleIndex < inputSwimlanes.length ? inputSwimlanes[circleIndex].color : historyItemGroupLocal; + circleIndex < inputSwimlanes.length ? inputSwimlanes[circleIndex].color : historyItemRefColor; let outputSwimlaneIndex = 0; for (let index = 0; index < inputSwimlanes.length; index++) { @@ -315,7 +315,7 @@ export function toISCMHistoryItemViewModelArray(historyItems: ISCMHistoryItem[], // Circle color - use the output swimlane color if present, otherwise the input swimlane color color = circleIndex < outputSwimlanes.length ? outputSwimlanes[circleIndex].color : - circleIndex < inputSwimlanes.length ? inputSwimlanes[circleIndex].color : historyItemGroupLocal; + circleIndex < inputSwimlanes.length ? inputSwimlanes[circleIndex].color : historyItemRefColor; } return { ...ref, color }; diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 52a30c7f6ce..6ef3cfb17a5 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -129,9 +129,9 @@ class SCMHistoryItemRefsActionViewItem extends ActionViewItem { const historyProvider = this._repository.provider.historyProvider.get(); return [ - historyProvider?.currentHistoryItemRef.get()?.name, - historyProvider?.currentHistoryItemRemoteRef.get()?.name, - historyProvider?.currentHistoryItemBaseRef.get()?.name + historyProvider?.historyItemRef.get()?.name, + historyProvider?.historyItemRemoteRef.get()?.name, + historyProvider?.historyItemBaseRef.get()?.name ].filter(ref => !!ref).join(', '); } else if (this._historyItemsFilter.length === 1) { return this._historyItemsFilter[0].name; @@ -326,7 +326,7 @@ class HistoryItemRenderer implements ITreeRenderer !!ref); break; default: @@ -801,9 +801,9 @@ class SCMHistoryViewModel extends Disposable { private _getGraphColorMap(historyItemRefs: ISCMHistoryItemRef[]): Map { const repository = this.repository.get(); const historyProvider = repository?.provider.historyProvider.get(); - const historyItemRef = historyProvider?.currentHistoryItemRef.get(); - const historyItemRemoteRef = historyProvider?.currentHistoryItemRemoteRef.get(); - const historyItemBaseRef = historyProvider?.currentHistoryItemBaseRef.get(); + const historyItemRef = historyProvider?.historyItemRef.get(); + const historyItemRemoteRef = historyProvider?.historyItemRemoteRef.get(); + const historyItemBaseRef = historyProvider?.historyItemBaseRef.get(); const colorMap = new Map(); @@ -1086,7 +1086,7 @@ export class SCMHistoryViewPane extends ViewPane { const firstRepositoryInitialized = derived(this, reader => { const repository = this._treeViewModel.repository.read(reader); const historyProvider = repository?.provider.historyProvider.read(reader); - const historyItemRef = historyProvider?.currentHistoryItemRef.read(reader); + const historyItemRef = historyProvider?.historyItemRef.read(reader); return historyItemRef !== undefined ? true : undefined; }); @@ -1115,12 +1115,12 @@ export class SCMHistoryViewPane extends ViewPane { // Publish const historyItemRemoteRefIdSignal = signalFromObservable(this, derived(reader => { - return historyProvider.currentHistoryItemRemoteRef.read(reader)?.id; + return historyProvider.historyItemRemoteRef.read(reader)?.id; })); // Fetch, Push const historyItemRemoteRefRevision = derived(reader => { - return historyProvider.currentHistoryItemRemoteRef.read(reader)?.revision; + return historyProvider.historyItemRemoteRef.read(reader)?.revision; }); // HistoryItemRefs changed @@ -1134,7 +1134,7 @@ export class SCMHistoryViewPane extends ViewPane { }, }, (reader, changeSummary) => { historyItemRemoteRefIdSignal.read(reader); - const historyItemRefValue = historyProvider.currentHistoryItemRef.read(reader); + const historyItemRefValue = historyProvider.historyItemRef.read(reader); const historyItemRemoteRefRevisionValue = historyItemRemoteRefRevision.read(reader); // Commit, Checkout, Publish, Pull diff --git a/src/vs/workbench/contrib/scm/browser/workingSet.ts b/src/vs/workbench/contrib/scm/browser/workingSet.ts index 7dfd2c00bd5..1185f8de20a 100644 --- a/src/vs/workbench/contrib/scm/browser/workingSet.ts +++ b/src/vs/workbench/contrib/scm/browser/workingSet.ts @@ -63,17 +63,17 @@ export class SCMWorkingSetController extends Disposable implements IWorkbenchCon private _onDidAddRepository(repository: ISCMRepository): void { const disposables = new DisposableStore(); - const currentHistoryItemGroupId = derived(reader => { + const historyItemRefId = derived(reader => { const historyProvider = repository.provider.historyProvider.read(reader); - const currentHistoryItemGroup = historyProvider?.currentHistoryItemGroup.read(reader); + const historyItemRef = historyProvider?.historyItemRef.read(reader); - return currentHistoryItemGroup?.id; + return historyItemRef?.id; }); disposables.add(autorun(async reader => { - const historyItemGroupId = currentHistoryItemGroupId.read(reader); + const historyItemRefIdValue = historyItemRefId.read(reader); - if (!historyItemGroupId) { + if (!historyItemRefIdValue) { return; } @@ -81,20 +81,20 @@ export class SCMWorkingSetController extends Disposable implements IWorkbenchCon const repositoryWorkingSets = this._workingSets.get(providerKey); if (!repositoryWorkingSets) { - this._workingSets.set(providerKey, { currentHistoryItemGroupId: historyItemGroupId, editorWorkingSets: new Map() }); + this._workingSets.set(providerKey, { currentHistoryItemGroupId: historyItemRefIdValue, editorWorkingSets: new Map() }); return; } // Editors for the current working set are automatically restored - if (repositoryWorkingSets.currentHistoryItemGroupId === historyItemGroupId) { + if (repositoryWorkingSets.currentHistoryItemGroupId === historyItemRefIdValue) { return; } // Save the working set - this._saveWorkingSet(providerKey, historyItemGroupId, repositoryWorkingSets); + this._saveWorkingSet(providerKey, historyItemRefIdValue, repositoryWorkingSets); // Restore the working set - await this._restoreWorkingSet(providerKey, historyItemGroupId); + await this._restoreWorkingSet(providerKey, historyItemRefIdValue); })); this._repositoryDisposables.set(repository, disposables); diff --git a/src/vs/workbench/contrib/scm/common/history.ts b/src/vs/workbench/contrib/scm/common/history.ts index 6a388fcb460..c40e5b214f2 100644 --- a/src/vs/workbench/contrib/scm/common/history.ts +++ b/src/vs/workbench/contrib/scm/common/history.ts @@ -15,11 +15,9 @@ export interface ISCMHistoryProviderMenus { } export interface ISCMHistoryProvider { - readonly currentHistoryItemGroup: IObservable; - - readonly currentHistoryItemRef: IObservable; - readonly currentHistoryItemRemoteRef: IObservable; - readonly currentHistoryItemBaseRef: IObservable; + readonly historyItemRef: IObservable; + readonly historyItemRemoteRef: IObservable; + readonly historyItemBaseRef: IObservable; readonly historyItemRefChanges: IObservable; @@ -35,14 +33,6 @@ export interface ISCMHistoryOptions { readonly historyItemRefs?: readonly string[]; } -export interface ISCMHistoryItemGroup { - readonly id: string; - readonly name: string; - readonly revision?: string; - readonly base?: Omit, 'remote'>; - readonly remote?: Omit, 'remote'>; -} - export interface ISCMHistoryItemStatistics { readonly files: number; readonly insertions: number; diff --git a/src/vs/workbench/contrib/scm/test/browser/scmHistory.test.ts b/src/vs/workbench/contrib/scm/test/browser/scmHistory.test.ts index 0cb64cfdda0..d322704bd79 100644 --- a/src/vs/workbench/contrib/scm/test/browser/scmHistory.test.ts +++ b/src/vs/workbench/contrib/scm/test/browser/scmHistory.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { ColorIdentifier } from '../../../../../platform/theme/common/colorUtils.js'; -import { colorRegistry, historyItemGroupBase, historyItemGroupLocal, historyItemGroupRemote, toISCMHistoryItemViewModelArray } from '../../browser/scmHistory.js'; +import { colorRegistry, historyItemBaseRefColor, historyItemRefColor, historyItemRemoteRefColor, toISCMHistoryItemViewModelArray } from '../../browser/scmHistory.js'; import { ISCMHistoryItem, ISCMHistoryItemRef } from '../../common/history.js'; function toSCMHistoryItem(id: string, parentIds: string[], references?: ISCMHistoryItemRef[]): ISCMHistoryItem { @@ -526,9 +526,9 @@ suite('toISCMHistoryItemViewModelArray', () => { ]; const colorMap = new Map([ - ['topic', historyItemGroupLocal], - ['origin/topic', historyItemGroupRemote], - ['origin/main', historyItemGroupBase], + ['topic', historyItemRefColor], + ['origin/topic', historyItemRemoteRefColor], + ['origin/main', historyItemBaseRefColor], ]); const viewModels = toISCMHistoryItemViewModelArray(models, colorMap); @@ -540,57 +540,57 @@ suite('toISCMHistoryItemViewModelArray', () => { assert.strictEqual(viewModels[0].outputSwimlanes.length, 1); assert.strictEqual(viewModels[0].outputSwimlanes[0].id, 'b'); - assert.strictEqual(viewModels[0].outputSwimlanes[0].color, historyItemGroupLocal); + assert.strictEqual(viewModels[0].outputSwimlanes[0].color, historyItemRefColor); // node b assert.strictEqual(viewModels[1].inputSwimlanes.length, 1); assert.strictEqual(viewModels[1].inputSwimlanes[0].id, 'b'); - assert.strictEqual(viewModels[1].inputSwimlanes[0].color, historyItemGroupLocal); + assert.strictEqual(viewModels[1].inputSwimlanes[0].color, historyItemRefColor); assert.strictEqual(viewModels[1].outputSwimlanes.length, 1); assert.strictEqual(viewModels[1].outputSwimlanes[0].id, 'c'); - assert.strictEqual(viewModels[1].outputSwimlanes[0].color, historyItemGroupLocal); + assert.strictEqual(viewModels[1].outputSwimlanes[0].color, historyItemRefColor); // node c assert.strictEqual(viewModels[2].inputSwimlanes.length, 1); assert.strictEqual(viewModels[2].inputSwimlanes[0].id, 'c'); - assert.strictEqual(viewModels[2].inputSwimlanes[0].color, historyItemGroupLocal); + assert.strictEqual(viewModels[2].inputSwimlanes[0].color, historyItemRefColor); assert.strictEqual(viewModels[2].outputSwimlanes.length, 1); assert.strictEqual(viewModels[2].outputSwimlanes[0].id, 'd'); - assert.strictEqual(viewModels[2].outputSwimlanes[0].color, historyItemGroupRemote); + assert.strictEqual(viewModels[2].outputSwimlanes[0].color, historyItemRemoteRefColor); // node d assert.strictEqual(viewModels[3].inputSwimlanes.length, 1); assert.strictEqual(viewModels[3].inputSwimlanes[0].id, 'd'); - assert.strictEqual(viewModels[3].inputSwimlanes[0].color, historyItemGroupRemote); + assert.strictEqual(viewModels[3].inputSwimlanes[0].color, historyItemRemoteRefColor); assert.strictEqual(viewModels[3].outputSwimlanes.length, 1); assert.strictEqual(viewModels[3].outputSwimlanes[0].id, 'e'); - assert.strictEqual(viewModels[3].outputSwimlanes[0].color, historyItemGroupRemote); + assert.strictEqual(viewModels[3].outputSwimlanes[0].color, historyItemRemoteRefColor); // node e assert.strictEqual(viewModels[4].inputSwimlanes.length, 1); assert.strictEqual(viewModels[4].inputSwimlanes[0].id, 'e'); - assert.strictEqual(viewModels[4].inputSwimlanes[0].color, historyItemGroupRemote); + assert.strictEqual(viewModels[4].inputSwimlanes[0].color, historyItemRemoteRefColor); assert.strictEqual(viewModels[4].outputSwimlanes.length, 2); assert.strictEqual(viewModels[4].outputSwimlanes[0].id, 'f'); - assert.strictEqual(viewModels[4].outputSwimlanes[0].color, historyItemGroupRemote); + assert.strictEqual(viewModels[4].outputSwimlanes[0].color, historyItemRemoteRefColor); assert.strictEqual(viewModels[4].outputSwimlanes[1].id, 'g'); - assert.strictEqual(viewModels[4].outputSwimlanes[1].color, historyItemGroupBase); + assert.strictEqual(viewModels[4].outputSwimlanes[1].color, historyItemBaseRefColor); // node g assert.strictEqual(viewModels[5].inputSwimlanes.length, 2); assert.strictEqual(viewModels[5].inputSwimlanes[0].id, 'f'); - assert.strictEqual(viewModels[5].inputSwimlanes[0].color, historyItemGroupRemote); + assert.strictEqual(viewModels[5].inputSwimlanes[0].color, historyItemRemoteRefColor); assert.strictEqual(viewModels[5].inputSwimlanes[1].id, 'g'); - assert.strictEqual(viewModels[5].inputSwimlanes[1].color, historyItemGroupBase); + assert.strictEqual(viewModels[5].inputSwimlanes[1].color, historyItemBaseRefColor); assert.strictEqual(viewModels[5].outputSwimlanes.length, 2); assert.strictEqual(viewModels[5].outputSwimlanes[0].id, 'f'); - assert.strictEqual(viewModels[5].outputSwimlanes[0].color, historyItemGroupRemote); + assert.strictEqual(viewModels[5].outputSwimlanes[0].color, historyItemRemoteRefColor); assert.strictEqual(viewModels[5].outputSwimlanes[1].id, 'h'); - assert.strictEqual(viewModels[5].outputSwimlanes[1].color, historyItemGroupBase); + assert.strictEqual(viewModels[5].outputSwimlanes[1].color, historyItemBaseRefColor); }); }); diff --git a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts index b6a4893dd54..cc1e6b44635 100644 --- a/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts @@ -11,13 +11,15 @@ declare module 'vscode' { } export interface SourceControlHistoryProvider { - currentHistoryItemGroup?: SourceControlHistoryItemGroup; + readonly currentHistoryItemRef: SourceControlHistoryItemRef | undefined; + readonly currentHistoryItemRemoteRef: SourceControlHistoryItemRef | undefined; + readonly currentHistoryItemBaseRef: SourceControlHistoryItemRef | undefined; /** - * Fires when the current history item group changes after - * a user action (ex: commit, checkout, fetch, pull, push) + * Fires when the current history item refs (local, remote, base) + * change after a user action (ex: commit, checkout, fetch, pull, push) */ - onDidChangeCurrentHistoryItemGroup: Event; + onDidChangeCurrentHistoryItemRefs: Event; /** * Fires when history item refs change @@ -37,14 +39,6 @@ declare module 'vscode' { readonly historyItemRefs?: readonly string[]; } - export interface SourceControlHistoryItemGroup { - readonly id: string; - readonly name: string; - readonly revision?: string; - readonly base?: Omit, 'remote'>; - readonly remote?: Omit, 'remote'>; - } - export interface SourceControlHistoryItemStatistics { readonly files: number; readonly insertions: number; From 04846aa8da4cb169d414d09f0220ce4d6aecbcae Mon Sep 17 00:00:00 2001 From: henricryden Date: Tue, 10 Sep 2024 14:50:30 +0200 Subject: [PATCH 34/45] additional search path for libc.so.6 in check-requirements-linux.sh (#227713) extra path for libc.so.6 in check-requirements-linux.sh --- resources/server/bin/helpers/check-requirements-linux.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index 31a618fbd85..8ef07a2fb1f 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -125,6 +125,9 @@ elif [ -z "$(ldd --version 2>&1 | grep 'musl libc')" ]; then elif [ -f /usr/lib/libc.so.6 ]; then # Typical path libc_path='/usr/lib/libc.so.6' + elif [ -f /lib64/libc.so.6 ]; then + # Typical path (OpenSUSE) + libc_path='/lib64/libc.so.6' elif [ -f /usr/lib64/libc.so.6 ]; then # Typical path libc_path='/usr/lib64/libc.so.6' From f6846e124eceeaad6ce5c8b3b7eb875e3f0b4eb0 Mon Sep 17 00:00:00 2001 From: Marcus Revaj Date: Tue, 10 Sep 2024 14:50:59 +0200 Subject: [PATCH 35/45] # Render file creation in the refactor preview (#226950) * # Render out the file edits --- .../browser/preview/bulkEditPreview.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts index 8b9a0c4eb65..ad2d7ebb97e 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts @@ -307,18 +307,26 @@ export class BulkFileOperations { return result; } - getFileEdits(uri: URI): ISingleEditOperation[] { + private async getFileEditOperation(edit: ResourceFileEdit): Promise { + const content = await edit.options.contents; + if (!content) { return undefined; } + return EditOperation.replaceMove(Range.lift({ startLineNumber: 0, startColumn: 0, endLineNumber: Number.MAX_VALUE, endColumn: 0 }), content.toString()); + } + + async getFileEdits(uri: URI): Promise { for (const file of this.fileOperations) { if (file.uri.toString() === uri.toString()) { - const result: ISingleEditOperation[] = []; + const result: Promise[] = []; let ignoreAll = false; for (const edit of file.originalEdits.values()) { - if (edit instanceof ResourceTextEdit) { + if (edit instanceof ResourceFileEdit) { + result.push(this.getFileEditOperation(edit)); + } else if (edit instanceof ResourceTextEdit) { if (this.checked.isChecked(edit)) { - result.push(EditOperation.replaceMove(Range.lift(edit.textEdit.range), !edit.textEdit.insertAsSnippet ? edit.textEdit.text : SnippetParser.asInsertText(edit.textEdit.text))); + result.push(Promise.resolve(EditOperation.replaceMove(Range.lift(edit.textEdit.range), !edit.textEdit.insertAsSnippet ? edit.textEdit.text : SnippetParser.asInsertText(edit.textEdit.text)))); } } else if (!this.checked.isChecked(edit)) { @@ -331,7 +339,7 @@ export class BulkFileOperations { return []; } - return result.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + return (await Promise.all(result)).filter(r => r !== undefined).sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); } } return []; @@ -402,7 +410,7 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { model.applyEdits(undoEdits); } // apply new edits and keep (future) undo edits - const newEdits = this._operations.getFileEdits(uri); + const newEdits = await this._operations.getFileEdits(uri); const newUndoEdits = model.applyEdits(newEdits, true); this._modelPreviewEdits.set(model.id, newUndoEdits); } From a7eeac01a84820f1de41fb9f1fddbcc29b7e2cb3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 10 Sep 2024 15:01:16 +0200 Subject: [PATCH 36/45] Hediet/b/slight-mink (#227793) * observable code restructuring * only import observables from the facade * introduces observableInternal/commonFacade to make extraction easier. * Fixes CI --- src/vs/base/common/observable.ts | 70 +------------ src/vs/base/common/observableInternal/api.ts | 7 +- .../base/common/observableInternal/autorun.ts | 4 +- src/vs/base/common/observableInternal/base.ts | 5 +- .../commonFacade/cancellation.ts | 7 ++ .../observableInternal/commonFacade/deps.ts | 10 ++ .../base/common/observableInternal/derived.ts | 7 +- .../base/common/observableInternal/index.ts | 29 ++++++ .../observableInternal/lazyObservableValue.ts | 5 +- .../base/common/observableInternal/promise.ts | 96 +----------------- .../base/common/observableInternal/utils.ts | 7 +- .../observableInternal/utilsCancellation.ts | 98 +++++++++++++++++++ src/vs/base/test/common/observable.test.ts | 7 +- src/vs/editor/browser/observableCodeEditor.ts | 6 +- .../treeSitter/treeSitterParserService.ts | 2 +- .../diffEditor/components/diffEditorSash.ts | 3 +- .../widget/diffEditor/diffEditorOptions.ts | 9 +- .../widget/diffEditor/diffEditorWidget.ts | 49 +++++----- .../diffEditor/features/gutterFeature.ts | 27 +++-- .../features/hideUnchangedRegionsFeature.ts | 19 ++-- .../multiDiffEditor/diffEditorItemTemplate.ts | 19 ++-- .../multiDiffEditorViewModel.ts | 15 ++- .../multiDiffEditor/multiDiffEditorWidget.ts | 20 ++-- .../multiDiffEditorWidgetImpl.ts | 27 +++-- .../browser/controller/commands.ts | 19 ++-- .../controller/inlineCompletionsController.ts | 5 +- .../inlineCompletionsHintsWidget.ts | 19 ++-- .../browser/inlineEditController.ts | 29 +++--- .../browser/inlineEditSideBySideWidget.ts | 7 +- .../contrib/inlineEdits/browser/commands.ts | 9 +- .../browser/inlineEditsController.ts | 13 ++- .../inlineEdits/browser/inlineEditsModel.ts | 3 +- .../inlineEdits/browser/inlineEditsWidget.ts | 11 +-- .../browser/placeholderTextContribution.ts | 4 +- .../browser/accessibilitySignalService.ts | 3 +- .../common/platformObservableUtils.ts | 3 +- .../editorTextPropertySignalsContribution.ts | 7 +- .../contrib/chat/common/chatAgents.ts | 3 +- .../browser/multiDiffEditorInput.ts | 7 +- .../browser/scmMultiDiffSourceResolver.ts | 9 +- .../contrib/scm/browser/scmHistoryViewPane.ts | 3 +- .../contrib/scm/browser/scmViewService.ts | 2 +- 42 files changed, 323 insertions(+), 381 deletions(-) create mode 100644 src/vs/base/common/observableInternal/commonFacade/cancellation.ts create mode 100644 src/vs/base/common/observableInternal/commonFacade/deps.ts create mode 100644 src/vs/base/common/observableInternal/index.ts create mode 100644 src/vs/base/common/observableInternal/utilsCancellation.ts diff --git a/src/vs/base/common/observable.ts b/src/vs/base/common/observable.ts index 1b8cecf7e69..86ac5971a27 100644 --- a/src/vs/base/common/observable.ts +++ b/src/vs/base/common/observable.ts @@ -5,72 +5,4 @@ // This is a facade for the observable implementation. Only import from here! -export type { - IObservable, - IObserver, - IReader, - ISettable, - ISettableObservable, - ITransaction, - IChangeContext, - IChangeTracker, -} from './observableInternal/base.js'; - -export { - observableValue, - disposableObservableValue, - transaction, - subtransaction, -} from './observableInternal/base.js'; -export { - derived, - derivedOpts, - derivedHandleChanges, - derivedWithStore, -} from './observableInternal/derived.js'; -export { - autorun, - autorunDelta, - autorunHandleChanges, - autorunWithStore, - autorunOpts, - autorunWithStoreHandleChanges, -} from './observableInternal/autorun.js'; -export type { - IObservableSignal, -} from './observableInternal/utils.js'; -export { - constObservable, - debouncedObservable, - derivedObservableWithCache, - derivedObservableWithWritableCache, - keepObserved, - recomputeInitiallyAndOnChange, - observableFromEvent, - observableFromPromise, - observableSignal, - observableSignalFromEvent, - wasEventTriggeredRecently, -} from './observableInternal/utils.js'; -export { - ObservableLazy, - ObservableLazyPromise, - ObservablePromise, - PromiseResult, - waitForState, - derivedWithCancellationToken, -} from './observableInternal/promise.js'; -export { - observableValueOpts -} from './observableInternal/api.js'; - -import { ConsoleObservableLogger, setLogger } from './observableInternal/logging.js'; - -// Remove "//" in the next line to enable logging -const enableLogging = false - // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this - ; - -if (enableLogging) { - setLogger(new ConsoleObservableLogger()); -} +export * from './observableInternal/index.js'; diff --git a/src/vs/base/common/observableInternal/api.ts b/src/vs/base/common/observableInternal/api.ts index 57cb2a5994d..0f7e63c3fa9 100644 --- a/src/vs/base/common/observableInternal/api.ts +++ b/src/vs/base/common/observableInternal/api.ts @@ -3,10 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EqualityComparer, strictEquals } from '../equals.js'; -import { ISettableObservable } from '../observable.js'; -import { ObservableValue } from './base.js'; -import { IDebugNameData, DebugNameData } from './debugName.js'; +import { ISettableObservable, ObservableValue } from './base.js'; +import { DebugNameData, IDebugNameData } from './debugName.js'; +import { EqualityComparer, strictEquals } from './commonFacade/deps.js'; import { LazyObservableValue } from './lazyObservableValue.js'; export function observableValueOpts( diff --git a/src/vs/base/common/observableInternal/autorun.ts b/src/vs/base/common/observableInternal/autorun.ts index dba2eb7edf3..f2011ef433d 100644 --- a/src/vs/base/common/observableInternal/autorun.ts +++ b/src/vs/base/common/observableInternal/autorun.ts @@ -3,11 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assertFn } from '../assert.js'; -import { onBugIndicatingError } from '../errors.js'; -import { DisposableStore, IDisposable, markAsDisposed, toDisposable, trackDisposable } from '../lifecycle.js'; import { IChangeContext, IObservable, IObserver, IReader } from './base.js'; import { DebugNameData, IDebugNameData } from './debugName.js'; +import { assertFn, DisposableStore, IDisposable, markAsDisposed, onBugIndicatingError, toDisposable, trackDisposable } from './commonFacade/deps.js'; import { getLogger } from './logging.js'; /** diff --git a/src/vs/base/common/observableInternal/base.ts b/src/vs/base/common/observableInternal/base.ts index 6821b4f3934..3ce43ead2ae 100644 --- a/src/vs/base/common/observableInternal/base.ts +++ b/src/vs/base/common/observableInternal/base.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { strictEquals, EqualityComparer } from '../equals.js'; -import { DisposableStore, IDisposable } from '../lifecycle.js'; -import { keepObserved, recomputeInitiallyAndOnChange } from '../observable.js'; 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 { keepObserved, recomputeInitiallyAndOnChange } from './utils.js'; /** * Represents an observable value. diff --git a/src/vs/base/common/observableInternal/commonFacade/cancellation.ts b/src/vs/base/common/observableInternal/commonFacade/cancellation.ts new file mode 100644 index 00000000000..af1686a3206 --- /dev/null +++ b/src/vs/base/common/observableInternal/commonFacade/cancellation.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export { CancellationError } from '../../errors.js'; +export { CancellationToken, CancellationTokenSource } from '../../cancellation.js'; diff --git a/src/vs/base/common/observableInternal/commonFacade/deps.ts b/src/vs/base/common/observableInternal/commonFacade/deps.ts new file mode 100644 index 00000000000..1ed8b1cc594 --- /dev/null +++ b/src/vs/base/common/observableInternal/commonFacade/deps.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export { assertFn } from '../../assert.js'; +export { type EqualityComparer, strictEquals } from '../../equals.js'; +export { BugIndicatingError, onBugIndicatingError } from '../../errors.js'; +export { Event, type IValueWithChangeEvent } from '../../event.js'; +export { DisposableStore, type IDisposable, markAsDisposed, toDisposable, trackDisposable } from '../../lifecycle.js'; diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index 88a060eb694..ed32875f8ba 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -3,12 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assertFn } from '../assert.js'; -import { EqualityComparer, strictEquals } from '../equals.js'; -import { onBugIndicatingError } from '../errors.js'; -import { DisposableStore, IDisposable } from '../lifecycle.js'; import { BaseObservable, IChangeContext, IObservable, IObserver, IReader, ISettableObservable, ITransaction, _setDerivedOpts, } from './base.js'; -import { DebugNameData, IDebugNameData, DebugOwner } from './debugName.js'; +import { DebugNameData, DebugOwner, IDebugNameData } from './debugName.js'; +import { DisposableStore, EqualityComparer, IDisposable, assertFn, onBugIndicatingError, strictEquals } from './commonFacade/deps.js'; import { getLogger } from './logging.js'; /** diff --git a/src/vs/base/common/observableInternal/index.ts b/src/vs/base/common/observableInternal/index.ts new file mode 100644 index 00000000000..9295f2697d5 --- /dev/null +++ b/src/vs/base/common/observableInternal/index.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// This is a facade for the observable implementation. Only import from here! + +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 { derived, derivedDisposable, derivedHandleChanges, derivedOpts, derivedWithSetter, derivedWithStore } from './derived.js'; +export { ObservableLazy, ObservableLazyPromise, ObservablePromise, PromiseResult, } from './promise.js'; +export { derivedWithCancellationToken, waitForState } from './utilsCancellation.js'; +export { constObservable, debouncedObservable, derivedConstOnceDefined, derivedObservableWithCache, derivedObservableWithWritableCache, keepObserved, latestChangedValue, mapObservableArrayCached, observableFromEvent, observableFromEventOpts, observableFromPromise, observableFromValueWithChangeEvent, observableSignal, observableSignalFromEvent, recomputeInitiallyAndOnChange, runOnChange, runOnChangeWithStore, signalFromObservable, ValueWithChangeEventFromObservable, wasEventTriggeredRecently, type IObservableSignal, } from './utils.js'; +export { type DebugOwner } from './debugName.js'; + +import { + ConsoleObservableLogger, + setLogger +} from './logging.js'; + +// Remove "//" in the next line to enable logging +const enableLogging = false + // || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this + ; + +if (enableLogging) { + setLogger(new ConsoleObservableLogger()); +} diff --git a/src/vs/base/common/observableInternal/lazyObservableValue.ts b/src/vs/base/common/observableInternal/lazyObservableValue.ts index 363fd7f8c5a..8a3f63c05d7 100644 --- a/src/vs/base/common/observableInternal/lazyObservableValue.ts +++ b/src/vs/base/common/observableInternal/lazyObservableValue.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EqualityComparer } from '../equals.js'; -import { ISettableObservable, ITransaction } from '../observable.js'; -import { BaseObservable, IObserver, TransactionImpl } from './base.js'; +import { EqualityComparer } from './commonFacade/deps.js'; +import { BaseObservable, IObserver, ISettableObservable, ITransaction, TransactionImpl } from './base.js'; import { DebugNameData } from './debugName.js'; /** diff --git a/src/vs/base/common/observableInternal/promise.ts b/src/vs/base/common/observableInternal/promise.ts index 6989a58731f..6551c21664c 100644 --- a/src/vs/base/common/observableInternal/promise.ts +++ b/src/vs/base/common/observableInternal/promise.ts @@ -2,13 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { autorun } from './autorun.js'; -import { IObservable, IReader, observableValue, transaction } from './base.js'; -import { Derived, derived } from './derived.js'; -import { CancellationToken, CancellationTokenSource } from '../cancellation.js'; -import { DebugNameData, DebugOwner } from './debugName.js'; -import { strictEquals } from '../equals.js'; -import { CancellationError } from '../errors.js'; +import { IObservable, observableValue, transaction } from './base.js'; +import { derived } from './derived.js'; export class ObservableLazy { private readonly _value = observableValue(this, undefined); @@ -120,90 +115,3 @@ export class ObservableLazyPromise { return this._lazyValue.getValue().promise; } } - -/** - * Resolves the promise when the observables state matches the predicate. - */ -export function waitForState(observable: IObservable): Promise; -export function waitForState(observable: IObservable, predicate: (state: T) => state is TState, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise; -export function waitForState(observable: IObservable, predicate: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise; -export function waitForState(observable: IObservable, predicate?: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise { - if (!predicate) { - predicate = state => state !== null && state !== undefined; - } - return new Promise((resolve, reject) => { - let isImmediateRun = true; - let shouldDispose = false; - const stateObs = observable.map(state => { - /** @description waitForState.state */ - return { - isFinished: predicate(state), - error: isError ? isError(state) : false, - state - }; - }); - const d = autorun(reader => { - /** @description waitForState */ - const { isFinished, error, state } = stateObs.read(reader); - if (isFinished || error) { - if (isImmediateRun) { - // The variable `d` is not initialized yet - shouldDispose = true; - } else { - d.dispose(); - } - if (error) { - reject(error === true ? state : error); - } else { - resolve(state); - } - } - }); - if (cancellationToken) { - const dc = cancellationToken.onCancellationRequested(() => { - d.dispose(); - dc.dispose(); - reject(new CancellationError()); - }); - if (cancellationToken.isCancellationRequested) { - d.dispose(); - dc.dispose(); - reject(new CancellationError()); - return; - } - } - isImmediateRun = false; - if (shouldDispose) { - d.dispose(); - } - }); -} - -export function derivedWithCancellationToken(computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable; -export function derivedWithCancellationToken(owner: object, computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable; -export function derivedWithCancellationToken(computeFnOrOwner: ((reader: IReader, cancellationToken: CancellationToken) => T) | object, computeFnOrUndefined?: ((reader: IReader, cancellationToken: CancellationToken) => T)): IObservable { - let computeFn: (reader: IReader, store: CancellationToken) => T; - let owner: DebugOwner; - if (computeFnOrUndefined === undefined) { - computeFn = computeFnOrOwner as any; - owner = undefined; - } else { - owner = computeFnOrOwner; - computeFn = computeFnOrUndefined as any; - } - - let cancellationTokenSource: CancellationTokenSource | undefined = undefined; - return new Derived( - new DebugNameData(owner, undefined, computeFn), - r => { - if (cancellationTokenSource) { - cancellationTokenSource.dispose(true); - } - cancellationTokenSource = new CancellationTokenSource(); - return computeFn(r, cancellationTokenSource.token); - }, undefined, - undefined, - () => cancellationTokenSource?.dispose(), - strictEquals, - ); -} diff --git a/src/vs/base/common/observableInternal/utils.ts b/src/vs/base/common/observableInternal/utils.ts index 3ce3805792e..47478220dae 100644 --- a/src/vs/base/common/observableInternal/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -3,15 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, IValueWithChangeEvent } from '../event.js'; -import { DisposableStore, IDisposable, toDisposable } from '../lifecycle.js'; import { autorun, autorunOpts, autorunWithStoreHandleChanges } from './autorun.js'; import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, _setKeepObserved, _setRecomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from './base.js'; -import { DebugNameData, IDebugNameData, DebugOwner, getDebugName, } from './debugName.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'; import { getLogger } from './logging.js'; -import { BugIndicatingError } from '../errors.js'; -import { EqualityComparer, strictEquals } from '../equals.js'; /** * Represents an efficient observable whose value never changes. diff --git a/src/vs/base/common/observableInternal/utilsCancellation.ts b/src/vs/base/common/observableInternal/utilsCancellation.ts new file mode 100644 index 00000000000..17e4ba9e308 --- /dev/null +++ b/src/vs/base/common/observableInternal/utilsCancellation.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 { IReader, IObservable } from './base.js'; +import { DebugOwner, DebugNameData } from './debugName.js'; +import { CancellationError, CancellationToken, CancellationTokenSource } from './commonFacade/cancellation.js'; +import { Derived } from './derived.js'; +import { strictEquals } from './commonFacade/deps.js'; +import { autorun } from './autorun.js'; + +/** + * Resolves the promise when the observables state matches the predicate. + */ +export function waitForState(observable: IObservable): Promise; +export function waitForState(observable: IObservable, predicate: (state: T) => state is TState, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise; +export function waitForState(observable: IObservable, predicate: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise; +export function waitForState(observable: IObservable, predicate?: (state: T) => boolean, isError?: (state: T) => boolean | unknown | undefined, cancellationToken?: CancellationToken): Promise { + if (!predicate) { + predicate = state => state !== null && state !== undefined; + } + return new Promise((resolve, reject) => { + let isImmediateRun = true; + let shouldDispose = false; + const stateObs = observable.map(state => { + /** @description waitForState.state */ + return { + isFinished: predicate(state), + error: isError ? isError(state) : false, + state + }; + }); + const d = autorun(reader => { + /** @description waitForState */ + const { isFinished, error, state } = stateObs.read(reader); + if (isFinished || error) { + if (isImmediateRun) { + // The variable `d` is not initialized yet + shouldDispose = true; + } else { + d.dispose(); + } + if (error) { + reject(error === true ? state : error); + } else { + resolve(state); + } + } + }); + if (cancellationToken) { + const dc = cancellationToken.onCancellationRequested(() => { + d.dispose(); + dc.dispose(); + reject(new CancellationError()); + }); + if (cancellationToken.isCancellationRequested) { + d.dispose(); + dc.dispose(); + reject(new CancellationError()); + return; + } + } + isImmediateRun = false; + if (shouldDispose) { + d.dispose(); + } + }); +} + +export function derivedWithCancellationToken(computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable; +export function derivedWithCancellationToken(owner: object, computeFn: (reader: IReader, cancellationToken: CancellationToken) => T): IObservable; +export function derivedWithCancellationToken(computeFnOrOwner: ((reader: IReader, cancellationToken: CancellationToken) => T) | object, computeFnOrUndefined?: ((reader: IReader, cancellationToken: CancellationToken) => T)): IObservable { + let computeFn: (reader: IReader, store: CancellationToken) => T; + let owner: DebugOwner; + if (computeFnOrUndefined === undefined) { + computeFn = computeFnOrOwner as any; + owner = undefined; + } else { + owner = computeFnOrOwner; + computeFn = computeFnOrUndefined as any; + } + + let cancellationTokenSource: CancellationTokenSource | undefined = undefined; + return new Derived( + new DebugNameData(owner, undefined, computeFn), + r => { + if (cancellationTokenSource) { + cancellationTokenSource.dispose(true); + } + cancellationTokenSource = new CancellationTokenSource(); + return computeFn(r, cancellationTokenSource.token); + }, undefined, + undefined, + () => cancellationTokenSource?.dispose(), + strictEquals + ); +} diff --git a/src/vs/base/test/common/observable.test.ts b/src/vs/base/test/common/observable.test.ts index 306f2a43a79..91d09493c15 100644 --- a/src/vs/base/test/common/observable.test.ts +++ b/src/vs/base/test/common/observable.test.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; +import { setUnexpectedErrorHandler } from '../../common/errors.js'; import { Emitter, Event } from '../../common/event.js'; import { DisposableStore } from '../../common/lifecycle.js'; -import { ISettableObservable, autorun, derived, ITransaction, observableFromEvent, observableValue, transaction, keepObserved, waitForState, autorunHandleChanges, observableSignal } from '../../common/observable.js'; -import { BaseObservable, IObservable, IObserver } from '../../common/observableInternal/base.js'; -import { derivedDisposable } from '../../common/observableInternal/derived.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 { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; -import { setUnexpectedErrorHandler } from '../../common/errors.js'; suite('observables', () => { const ds = ensureNoDisposablesAreLeakedInTestSuite(); diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index 868f6bca9aa..9641c87228b 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -5,16 +5,14 @@ import { equalsIfDefined, itemsEquals } from '../../base/common/equals.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js'; -import { IObservable, ITransaction, autorun, autorunOpts, derived, derivedOpts, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.js'; -import { TransactionImpl } from '../../base/common/observableInternal/base.js'; -import { derivedWithSetter } from '../../base/common/observableInternal/derived.js'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from './editorBrowser.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 { Position } from '../common/core/position.js'; 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'; /** * Returns a facade for the code editor that provides observables for various states/events. diff --git a/src/vs/editor/browser/services/treeSitter/treeSitterParserService.ts b/src/vs/editor/browser/services/treeSitter/treeSitterParserService.ts index e6d3695efac..26a61c41c1a 100644 --- a/src/vs/editor/browser/services/treeSitter/treeSitterParserService.ts +++ b/src/vs/editor/browser/services/treeSitter/treeSitterParserService.ts @@ -21,7 +21,7 @@ import { CancellationToken, cancelOnDispose } from '../../../../base/common/canc import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; import { canASAR } from '../../../../base/common/amd.js'; import { CancellationError, isCancellationError } from '../../../../base/common/errors.js'; -import { PromiseResult } from '../../../../base/common/observableInternal/promise.js'; +import { PromiseResult } from '../../../../base/common/observable.js'; const EDITOR_TREESITTER_TELEMETRY = 'editor.experimental.treeSitterTelemetry'; const MODULE_LOCATION_SUBPATH = `@vscode/tree-sitter-wasm/wasm`; diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts index beb8bcc0761..8abaddcceb9 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts @@ -5,9 +5,8 @@ import { IBoundarySashes, ISashEvent, Orientation, Sash, SashState } from '../../../../../base/browser/ui/sash/sash.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { IObservable, IReader, ISettableObservable, autorun, observableValue } from '../../../../../base/common/observable.js'; +import { IObservable, IReader, ISettableObservable, autorun, derivedWithSetter, observableValue } from '../../../../../base/common/observable.js'; import { DiffEditorOptions } from '../diffEditorOptions.js'; -import { derivedWithSetter } from '../../../../../base/common/observableInternal/derived.js'; export class SashLayout { public readonly sashLeft = derivedWithSetter(this, reader => { diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts index 97260fa8c83..0c1a533681f 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts @@ -3,15 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IObservable, ISettableObservable, derived, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; -import { derivedConstOnceDefined } from '../../../../base/common/observableInternal/utils.js'; +import { IObservable, ISettableObservable, derived, derivedConstOnceDefined, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { Constants } from '../../../../base/common/uint.js'; -import { allowsTrueInlineDiffRendering } from './components/diffEditorViewZones/diffEditorViewZones.js'; -import { DiffEditorViewModel, DiffState } from './diffEditorViewModel.js'; +import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { diffEditorDefaultOptions } from '../../../common/config/diffEditor.js'; import { IDiffEditorBaseOptions, IDiffEditorOptions, IEditorOptions, ValidDiffEditorBaseOptions, clampedFloat, clampedInt, boolean as validateBooleanOption, stringSet as validateStringSetOption } from '../../../common/config/editorOptions.js'; import { LineRangeMapping } from '../../../common/diff/rangeMapping.js'; -import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; +import { allowsTrueInlineDiffRendering } from './components/diffEditorViewZones/diffEditorViewZones.js'; +import { DiffEditorViewModel, DiffState } from './diffEditorViewModel.js'; export class DiffEditorOptions { private readonly _options: ISettableObservable, { changedOptions: IDiffEditorOptions }>; diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 6b69885c757..da2e28f1c44 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -7,28 +7,15 @@ import { IBoundarySashes } from '../../../../base/browser/ui/sash/sash.js'; import { findLast } from '../../../../base/common/arraysFind.js'; import { BugIndicatingError, onUnexpectedError } from '../../../../base/common/errors.js'; import { Event } from '../../../../base/common/event.js'; -import { toDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, ITransaction, autorun, autorunWithStore, derived, disposableObservableValue, observableFromEvent, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from '../../../../base/common/observable.js'; -import { derivedDisposable } from '../../../../base/common/observableInternal/derived.js'; -import './style.css'; -import { IEditorConstructionOptions } from '../../config/editorConfiguration.js'; -import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from '../../editorBrowser.js'; -import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from '../../editorExtensions.js'; -import { ICodeEditorService } from '../../services/codeEditorService.js'; -import { StableEditorScrollState } from '../../stableEditorScroll.js'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../codeEditor/codeEditorWidget.js'; -import { AccessibleDiffViewer, AccessibleDiffViewerModelFromEditors } from './components/accessibleDiffViewer.js'; -import { DiffEditorDecorations } from './components/diffEditorDecorations.js'; -import { DiffEditorSash, SashLayout } from './components/diffEditorSash.js'; -import { DiffEditorViewZones } from './components/diffEditorViewZones/diffEditorViewZones.js'; -import { DiffEditorGutter } from './features/gutterFeature.js'; -import { HideUnchangedRegionsFeature } from './features/hideUnchangedRegionsFeature.js'; -import { MovedBlocksLinesFeature } from './features/movedBlocksLinesFeature.js'; -import { OverviewRulerFeature } from './features/overviewRulerFeature.js'; -import { RevertButtonsFeature } from './features/revertButtonsFeature.js'; -import { CSSStyle, ObservableElementSizeObserver, RefCounted, applyStyle, applyViewZones, translatePosition } from './utils.js'; import { readHotReloadableExport } from '../../../../base/common/hotReloadHelpers.js'; +import { toDisposable } from '../../../../base/common/lifecycle.js'; +import { IObservable, ITransaction, autorun, autorunWithStore, derived, derivedDisposable, disposableObservableValue, observableFromEvent, observableValue, recomputeInitiallyAndOnChange, subtransaction, transaction } from '../../../../base/common/observable.js'; +import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; +import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; +import { IEditorProgressService } from '../../../../platform/progress/common/progress.js'; import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; import { IDimension } from '../../../common/core/dimension.js'; import { Position } from '../../../common/core/position.js'; @@ -39,15 +26,27 @@ import { LineRangeMapping, RangeMapping } from '../../../common/diff/rangeMappin import { EditorType, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { IIdentifiedSingleEditOperation } from '../../../common/model.js'; -import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; -import { IEditorProgressService } from '../../../../platform/progress/common/progress.js'; +import { IEditorConstructionOptions } from '../../config/editorConfiguration.js'; +import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from '../../editorBrowser.js'; +import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from '../../editorExtensions.js'; +import { ICodeEditorService } from '../../services/codeEditorService.js'; +import { StableEditorScrollState } from '../../stableEditorScroll.js'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../codeEditor/codeEditorWidget.js'; +import { AccessibleDiffViewer, AccessibleDiffViewerModelFromEditors } from './components/accessibleDiffViewer.js'; +import { DiffEditorDecorations } from './components/diffEditorDecorations.js'; import { DiffEditorEditors } from './components/diffEditorEditors.js'; +import { DiffEditorSash, SashLayout } from './components/diffEditorSash.js'; +import { DiffEditorViewZones } from './components/diffEditorViewZones/diffEditorViewZones.js'; import { DelegatingEditor } from './delegatingEditorImpl.js'; import { DiffEditorOptions } from './diffEditorOptions.js'; import { DiffEditorViewModel, DiffMapping, DiffState } from './diffEditorViewModel.js'; +import { DiffEditorGutter } from './features/gutterFeature.js'; +import { HideUnchangedRegionsFeature } from './features/hideUnchangedRegionsFeature.js'; +import { MovedBlocksLinesFeature } from './features/movedBlocksLinesFeature.js'; +import { OverviewRulerFeature } from './features/overviewRulerFeature.js'; +import { RevertButtonsFeature } from './features/revertButtonsFeature.js'; +import './style.css'; +import { CSSStyle, ObservableElementSizeObserver, RefCounted, applyStyle, applyViewZones, translatePosition } from './utils.js'; export interface IDiffCodeEditorWidgetOptions { originalEditor?: ICodeEditorWidgetOptions; diff --git a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts index a2a4eb15f27..556378aebf3 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts @@ -9,16 +9,13 @@ import { ActionsOrientation } from '../../../../../base/browser/ui/actionbar/act import { HoverPosition } from '../../../../../base/browser/ui/hover/hoverWidget.js'; import { IBoundarySashes } from '../../../../../base/browser/ui/sash/sash.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; -import { IObservable, autorun, autorunWithStore, derived, observableFromEvent, observableValue } from '../../../../../base/common/observable.js'; -import { derivedDisposable, derivedWithSetter } from '../../../../../base/common/observableInternal/derived.js'; +import { IObservable, autorun, autorunWithStore, derived, derivedDisposable, derivedWithSetter, observableFromEvent, observableValue } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; -import { DiffEditorEditors } from '../components/diffEditorEditors.js'; -import { DiffEditorSash, SashLayout } from '../components/diffEditorSash.js'; -import { DiffEditorOptions } from '../diffEditorOptions.js'; -import { DiffEditorViewModel } from '../diffEditorViewModel.js'; -import { appendRemoveOnDispose, applyStyle, prependRemoveOnDispose } from '../utils.js'; -import { EditorGutter, IGutterItemInfo, IGutterItemView } from '../utils/editorGutter.js'; -import { ActionRunnerWithContext } from '../../multiDiffEditor/utils.js'; +import { HiddenItemStrategy, MenuWorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; +import { IMenuService, MenuId } from '../../../../../platform/actions/common/actions.js'; +import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; +import { WorkbenchHoverDelegate } from '../../../../../platform/hover/browser/hover.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { LineRange, LineRangeSet } from '../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../common/core/offsetRange.js'; @@ -26,11 +23,13 @@ import { Range } from '../../../../common/core/range.js'; import { TextEdit } from '../../../../common/core/textEdit.js'; import { DetailedLineRangeMapping } from '../../../../common/diff/rangeMapping.js'; import { TextModelText } from '../../../../common/model/textModelText.js'; -import { HiddenItemStrategy, MenuWorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; -import { IMenuService, MenuId } from '../../../../../platform/actions/common/actions.js'; -import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; -import { WorkbenchHoverDelegate } from '../../../../../platform/hover/browser/hover.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ActionRunnerWithContext } from '../../multiDiffEditor/utils.js'; +import { DiffEditorEditors } from '../components/diffEditorEditors.js'; +import { DiffEditorSash, SashLayout } from '../components/diffEditorSash.js'; +import { DiffEditorOptions } from '../diffEditorOptions.js'; +import { DiffEditorViewModel } from '../diffEditorViewModel.js'; +import { appendRemoveOnDispose, applyStyle, prependRemoveOnDispose } from '../utils.js'; +import { EditorGutter, IGutterItemInfo, IGutterItemView } from '../utils/editorGutter.js'; const emptyArr: never[] = []; const width = 35; diff --git a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts index 559c40ec8eb..e87fe47e055 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts @@ -8,16 +8,11 @@ import { renderIcon, renderLabelWithIcons } from '../../../../../base/browser/ui import { Codicon } from '../../../../../base/common/codicons.js'; import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; -import { IObservable, IReader, autorun, derived, derivedWithStore, observableValue, transaction } from '../../../../../base/common/observable.js'; -import { derivedDisposable } from '../../../../../base/common/observableInternal/derived.js'; +import { IObservable, IReader, autorun, derived, derivedDisposable, derivedWithStore, observableValue, transaction } from '../../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { isDefined } from '../../../../../base/common/types.js'; -import { ICodeEditor } from '../../../editorBrowser.js'; -import { observableCodeEditor } from '../../../observableCodeEditor.js'; -import { DiffEditorEditors } from '../components/diffEditorEditors.js'; -import { DiffEditorOptions } from '../diffEditorOptions.js'; -import { DiffEditorViewModel, RevealPreference, UnchangedRegion } from '../diffEditorViewModel.js'; -import { IObservableViewZone, PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle } from '../utils.js'; +import { localize } from '../../../../../nls.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../common/core/lineRange.js'; import { Position } from '../../../../common/core/position.js'; @@ -25,8 +20,12 @@ import { Range } from '../../../../common/core/range.js'; import { CursorChangeReason } from '../../../../common/cursorEvents.js'; import { SymbolKind, SymbolKinds } from '../../../../common/languages.js'; import { IModelDecorationOptions, IModelDeltaDecoration, ITextModel } from '../../../../common/model.js'; -import { localize } from '../../../../../nls.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ICodeEditor } from '../../../editorBrowser.js'; +import { observableCodeEditor } from '../../../observableCodeEditor.js'; +import { DiffEditorEditors } from '../components/diffEditorEditors.js'; +import { DiffEditorOptions } from '../diffEditorOptions.js'; +import { DiffEditorViewModel, RevealPreference, UnchangedRegion } from '../diffEditorViewModel.js'; +import { IObservableViewZone, PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle } from '../utils.js'; /** * Make sure to add the view zones to the editor! diff --git a/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts b/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts index 6e35b12dbb0..48ac35815d4 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts @@ -6,22 +6,21 @@ import { h } from '../../../../base/browser/dom.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { autorun, derived } from '../../../../base/common/observable.js'; -import { globalTransaction, observableValue } from '../../../../base/common/observableInternal/base.js'; -import { observableCodeEditor } from '../../observableCodeEditor.js'; -import { DiffEditorWidget } from '../diffEditor/diffEditorWidget.js'; -import { DocumentDiffItemViewModel } from './multiDiffEditorViewModel.js'; -import { IWorkbenchUIElementFactory } from './workbenchUIElementFactory.js'; -import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; +import { autorun, derived, globalTransaction, observableValue } from '../../../../base/common/observable.js'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { IContextKeyService, type IScopedContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; +import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; +import { OffsetRange } from '../../../common/core/offsetRange.js'; +import { observableCodeEditor } from '../../observableCodeEditor.js'; +import { DiffEditorWidget } from '../diffEditor/diffEditorWidget.js'; +import { DocumentDiffItemViewModel } from './multiDiffEditorViewModel.js'; import { IObjectData, IPooledObject } from './objectPool.js'; import { ActionRunnerWithContext } from './utils.js'; -import { IContextKeyService, type IScopedContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; +import { IWorkbenchUIElementFactory } from './workbenchUIElementFactory.js'; export class TemplateData implements IObjectData { constructor( diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts index 518b4720698..49c674d0477 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts @@ -4,19 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, ITransaction, derived, observableValue, transaction } from '../../../../base/common/observable.js'; -import { constObservable, derivedObservableWithWritableCache, mapObservableArrayCached, observableFromValueWithChangeEvent } from '../../../../base/common/observableInternal/utils.js'; +import { IObservable, ITransaction, constObservable, derived, derivedObservableWithWritableCache, mapObservableArrayCached, observableFromValueWithChangeEvent, observableValue, transaction } from '../../../../base/common/observable.js'; import { URI } from '../../../../base/common/uri.js'; -import { DiffEditorOptions } from '../diffEditor/diffEditorOptions.js'; -import { DiffEditorViewModel } from '../diffEditor/diffEditorViewModel.js'; -import { RefCounted } from '../diffEditor/utils.js'; -import { IDocumentDiffItem, IMultiDiffEditorModel } from './model.js'; +import { ContextKeyValue } from '../../../../platform/contextkey/common/contextkey.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; import { Selection } from '../../../common/core/selection.js'; import { IDiffEditorViewModel } from '../../../common/editorCommon.js'; import { IModelService } from '../../../common/services/model.js'; -import { ContextKeyValue } from '../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { DiffEditorOptions } from '../diffEditor/diffEditorOptions.js'; +import { DiffEditorViewModel } from '../diffEditor/diffEditorViewModel.js'; +import { RefCounted } from '../diffEditor/utils.js'; +import { IDocumentDiffItem, IMultiDiffEditorModel } from './model.js'; export class MultiDiffEditorViewModel extends Disposable { private readonly _documents: IObservable[] | 'loading'> = observableFromValueWithChangeEvent(this.model, this.model.documents); diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts index 6236c75a2f8..d23ad8c87f6 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts @@ -4,22 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { Dimension } from '../../../../base/browser/dom.js'; +import { Event } from '../../../../base/common/event.js'; +import { readHotReloadableExport } from '../../../../base/common/hotReloadHelpers.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { derived, derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from '../../../../base/common/observable.js'; -import { readHotReloadableExport } from '../../../../base/common/hotReloadHelpers.js'; -import { IDocumentDiffItem, IMultiDiffEditorModel } from './model.js'; -import { IMultiDiffEditorViewState, IMultiDiffResourceId, MultiDiffEditorWidgetImpl } from './multiDiffEditorWidgetImpl.js'; -import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import './colors.js'; -import { DiffEditorItemTemplate } from './diffEditorItemTemplate.js'; -import { IWorkbenchUIElementFactory } from './workbenchUIElementFactory.js'; -import { Event } from '../../../../base/common/event.js'; import { URI } from '../../../../base/common/uri.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { Range } from '../../../common/core/range.js'; import { IDiffEditor } from '../../../common/editorCommon.js'; import { ICodeEditor } from '../../editorBrowser.js'; import { DiffEditorWidget } from '../diffEditor/diffEditorWidget.js'; -import { Range } from '../../../common/core/range.js'; +import './colors.js'; +import { DiffEditorItemTemplate } from './diffEditorItemTemplate.js'; +import { IDocumentDiffItem, IMultiDiffEditorModel } from './model.js'; +import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel.js'; +import { IMultiDiffEditorViewState, IMultiDiffResourceId, MultiDiffEditorWidgetImpl } from './multiDiffEditorWidgetImpl.js'; +import { IWorkbenchUIElementFactory } from './workbenchUIElementFactory.js'; export class MultiDiffEditorWidget extends Disposable { private readonly _dimension = observableValue(this, undefined); diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts index 4069feb23d1..ec0bf3cf64c 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts @@ -9,29 +9,28 @@ import { compareBy, numberComparator } from '../../../../base/common/arrays.js'; import { findFirstMax } from '../../../../base/common/arraysFind.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; import { Disposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, IReader, autorun, autorunWithStore, derived, derivedWithStore, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; -import { ITransaction, disposableObservableValue, globalTransaction, transaction } from '../../../../base/common/observableInternal/base.js'; +import { IObservable, IReader, ITransaction, autorun, autorunWithStore, derived, derivedWithStore, disposableObservableValue, globalTransaction, observableFromEvent, observableValue, transaction } from '../../../../base/common/observable.js'; import { Scrollable, ScrollbarVisibility } from '../../../../base/common/scrollable.js'; import { URI } from '../../../../base/common/uri.js'; -import './style.css'; -import { ICodeEditor } from '../../editorBrowser.js'; -import { ObservableElementSizeObserver } from '../diffEditor/utils.js'; -import { RevealOptions } from './multiDiffEditorWidget.js'; -import { IWorkbenchUIElementFactory } from './workbenchUIElementFactory.js'; +import { localize } from '../../../../nls.js'; +import { ContextKeyValue, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { OffsetRange } from '../../../common/core/offsetRange.js'; import { IRange } from '../../../common/core/range.js'; import { ISelection, Selection } from '../../../common/core/selection.js'; import { IDiffEditor } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ContextKeyValue, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; +import { ICodeEditor } from '../../editorBrowser.js'; +import { ObservableElementSizeObserver } from '../diffEditor/utils.js'; import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate.js'; -import { DocumentDiffItemViewModel, MultiDiffEditorViewModel } from './multiDiffEditorViewModel.js'; -import { ObjectPool } from './objectPool.js'; -import { localize } from '../../../../nls.js'; import { IDocumentDiffItem } from './model.js'; +import { DocumentDiffItemViewModel, MultiDiffEditorViewModel } from './multiDiffEditorViewModel.js'; +import { RevealOptions } from './multiDiffEditorWidget.js'; +import { ObjectPool } from './objectPool.js'; +import './style.css'; +import { IWorkbenchUIElementFactory } from './workbenchUIElementFactory.js'; export class MultiDiffEditorWidgetImpl extends Disposable { private readonly _scrollableElements = h('div.scrollContent', [ diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts index d41aea21727..595ab703f28 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts @@ -4,20 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; -import { transaction } from '../../../../../base/common/observable.js'; -import { asyncTransaction } from '../../../../../base/common/observableInternal/base.js'; -import { ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { EditorAction, ServicesAccessor } from '../../../../browser/editorExtensions.js'; -import { EditorContextKeys } from '../../../../common/editorContextKeys.js'; -import { showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId, inlineSuggestCommitId } from './commandIds.js'; -import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js'; -import { InlineCompletionsController } from './inlineCompletionsController.js'; -import { Context as SuggestContext } from '../../../suggest/browser/suggest.js'; +import { asyncTransaction, transaction } from '../../../../../base/common/observable.js'; import * as nls from '../../../../../nls.js'; -import { MenuId, Action2 } from '../../../../../platform/actions/common/actions.js'; +import { Action2, MenuId } from '../../../../../platform/actions/common/actions.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 { ICodeEditor } from '../../../../browser/editorBrowser.js'; +import { EditorAction, ServicesAccessor } from '../../../../browser/editorExtensions.js'; +import { EditorContextKeys } from '../../../../common/editorContextKeys.js'; +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'; export class ShowNextInlineSuggestionAction extends EditorAction { public static ID = showNextInlineSuggestionActionId; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 97064c64434..424715a2aab 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -9,10 +9,7 @@ import { timeout } from '../../../../../base/common/async.js'; import { cancelOnDispose } from '../../../../../base/common/cancellation.js'; import { readHotReloadableExport } from '../../../../../base/common/hotReloadHelpers.js'; import { Disposable, DisposableStore, toDisposable } from '../../../../../base/common/lifecycle.js'; -import { IObservable, ITransaction, autorun, constObservable, derived, observableFromEvent, observableSignal, observableValue, transaction, waitForState } from '../../../../../base/common/observable.js'; -import { ISettableObservable } from '../../../../../base/common/observableInternal/base.js'; -import { derivedDisposable } from '../../../../../base/common/observableInternal/derived.js'; -import { derivedObservableWithCache, mapObservableArrayCached, runOnChange, runOnChangeWithStore } from '../../../../../base/common/observableInternal/utils.js'; +import { IObservable, ISettableObservable, ITransaction, autorun, constObservable, derived, derivedDisposable, derivedObservableWithCache, mapObservableArrayCached, observableFromEvent, observableSignal, observableValue, runOnChange, runOnChangeWithStore, transaction, waitForState } from '../../../../../base/common/observable.js'; import { isUndefined } from '../../../../../base/common/types.js'; import { localize } from '../../../../../nls.js'; import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts index 7d613257c56..1625099889f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts @@ -11,18 +11,9 @@ import { equals } from '../../../../../base/common/arrays.js'; import { RunOnceScheduler } from '../../../../../base/common/async.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { Disposable, toDisposable } from '../../../../../base/common/lifecycle.js'; -import { IObservable, autorun, autorunWithStore, derived, derivedObservableWithCache, observableFromEvent } from '../../../../../base/common/observable.js'; -import { derivedWithStore } from '../../../../../base/common/observableInternal/derived.js'; +import { IObservable, autorun, autorunWithStore, derived, derivedObservableWithCache, derivedWithStore, observableFromEvent } from '../../../../../base/common/observable.js'; import { OS } from '../../../../../base/common/platform.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; -import './inlineCompletionsHintsWidget.css'; -import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../browser/editorBrowser.js'; -import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { Position } from '../../../../common/core/position.js'; -import { Command, InlineCompletionTriggerKind } from '../../../../common/languages.js'; -import { PositionAffinity } from '../../../../common/model.js'; -import { showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId } from '../controller/commandIds.js'; -import { InlineCompletionsModel } from '../model/inlineCompletionsModel.js'; import { localize } from '../../../../../nls.js'; import { MenuEntryActionViewItem, createAndFillInActionBarActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuWorkbenchToolBarOptions, WorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; @@ -34,6 +25,14 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { registerIcon } from '../../../../../platform/theme/common/iconRegistry.js'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../browser/editorBrowser.js'; +import { EditorOption } from '../../../../common/config/editorOptions.js'; +import { Position } from '../../../../common/core/position.js'; +import { Command, InlineCompletionTriggerKind } from '../../../../common/languages.js'; +import { PositionAffinity } from '../../../../common/model.js'; +import { showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId } from '../controller/commandIds.js'; +import { InlineCompletionsModel } from '../model/inlineCompletionsModel.js'; +import './inlineCompletionsHintsWidget.css'; export class InlineCompletionsHintsWidget extends Disposable { private readonly alwaysShowToolbar = observableFromEvent(this, this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.inlineSuggest).showToolbar === 'always'); diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts b/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts index 362fc07a365..b0cf6e51db8 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts @@ -3,29 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { createStyleSheet2 } from '../../../../base/browser/dom.js'; +import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; -import { ISettableObservable, autorun, constObservable, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from '../../../../base/common/observable.js'; +import { ISettableObservable, autorun, constObservable, derivedDisposable, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from '../../../../base/common/observable.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; +import { IDiffProviderFactoryService } from '../../../browser/widget/diffEditor/diffProviderFactoryService.js'; +import { EditorOption } from '../../../common/config/editorOptions.js'; import { EditOperation } from '../../../common/core/editOperation.js'; import { Position } from '../../../common/core/position.js'; import { Range } from '../../../common/core/range.js'; -import { GhostTextWidget } from './ghostTextWidget.js'; -import { IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IInlineEdit, InlineEditTriggerKind } from '../../../common/languages.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; -import { GhostText, GhostTextPart } from '../../inlineCompletions/browser/model/ghostText.js'; -import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { InlineEditHintsWidget } from './inlineEditHintsWidget.js'; -import { EditorOption } from '../../../common/config/editorOptions.js'; -import { createStyleSheet2 } from '../../../../base/browser/dom.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; -import { derivedDisposable } from '../../../../base/common/observableInternal/derived.js'; -import { InlineEditSideBySideWidget } from './inlineEditSideBySideWidget.js'; -import { IDiffProviderFactoryService } from '../../../browser/widget/diffEditor/diffProviderFactoryService.js'; import { IModelService } from '../../../common/services/model.js'; +import { GhostText, GhostTextPart } from '../../inlineCompletions/browser/model/ghostText.js'; +import { GhostTextWidget } from './ghostTextWidget.js'; +import { InlineEditHintsWidget } from './inlineEditHintsWidget.js'; +import { InlineEditSideBySideWidget } from './inlineEditSideBySideWidget.js'; export class InlineEditController extends Disposable { static ID = 'editor.contrib.inlineEditController'; diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts b/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts index a4daba0c44e..4655e81186d 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts @@ -6,10 +6,9 @@ import { $ } from '../../../../base/browser/dom.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, ObservablePromise, autorun, autorunWithStore, derived, observableSignalFromEvent } from '../../../../base/common/observable.js'; -import { derivedDisposable } from '../../../../base/common/observableInternal/derived.js'; +import { IObservable, ObservablePromise, autorun, autorunWithStore, derived, derivedDisposable, observableSignalFromEvent } from '../../../../base/common/observable.js'; import { URI } from '../../../../base/common/uri.js'; -import './inlineEditSideBySideWidget.css'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from '../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../browser/observableCodeEditor.js'; import { EmbeddedCodeEditorWidget } from '../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; @@ -24,7 +23,7 @@ import { PLAINTEXT_LANGUAGE_ID } from '../../../common/languages/modesRegistry.j import { IModelDeltaDecoration } from '../../../common/model.js'; import { TextModel } from '../../../common/model/textModel.js'; import { IModelService } from '../../../common/services/model.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import './inlineEditSideBySideWidget.css'; function* range(start: number, end: number, step = 1) { if (end === undefined) { [end, start] = [start, 0]; } diff --git a/src/vs/editor/contrib/inlineEdits/browser/commands.ts b/src/vs/editor/contrib/inlineEdits/browser/commands.ts index 4314aad0e5f..36ebdbf1aa1 100644 --- a/src/vs/editor/contrib/inlineEdits/browser/commands.ts +++ b/src/vs/editor/contrib/inlineEdits/browser/commands.ts @@ -5,17 +5,16 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; -import { transaction } from '../../../../base/common/observable.js'; -import { asyncTransaction } from '../../../../base/common/observableInternal/base.js'; +import { asyncTransaction, transaction } from '../../../../base/common/observable.js'; +import * as nls from '../../../../nls.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EmbeddedCodeEditorWidget } from '../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { inlineEditAcceptId, inlineEditVisible, showNextInlineEditActionId, showPreviousInlineEditActionId } from './consts.js'; import { InlineEditsController } from './inlineEditsController.js'; -import * as nls from '../../../../nls.js'; -import { MenuId } from '../../../../platform/actions/common/actions.js'; -import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; function labelAndAlias(str: nls.ILocalizedString): { label: string; alias: string } { diff --git a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts index 784c3d57231..893cb11d931 100644 --- a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts +++ b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts @@ -3,22 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { readHotReloadableExport } from '../../../../base/common/hotReloadHelpers.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { derived, derivedObservableWithCache, IReader, ISettableObservable, observableValue } from '../../../../base/common/observable.js'; -import { derivedDisposable, derivedWithSetter } from '../../../../base/common/observableInternal/derived.js'; +import { derived, derivedDisposable, derivedObservableWithCache, derivedWithSetter, IReader, ISettableObservable, observableValue } from '../../../../base/common/observable.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 { bindContextKey, observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../browser/observableCodeEditor.js'; -import { readHotReloadableExport } from '../../../../base/common/hotReloadHelpers.js'; import { Selection } from '../../../common/core/selection.js'; import { ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; import { inlineEditVisible, isPinnedContextKey } from './consts.js'; import { InlineEditsModel } from './inlineEditsModel.js'; import { InlineEditsWidget } from './inlineEditsWidget.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 { bindContextKey, observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; export class InlineEditsController extends Disposable { static ID = 'editor.contrib.inlineEditsController'; diff --git a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsModel.ts b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsModel.ts index 98e8b9909be..5b226750016 100644 --- a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsModel.ts +++ b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsModel.ts @@ -8,8 +8,7 @@ import { CancellationToken, cancelOnDispose } from '../../../../base/common/canc import { itemsEquals, structuralEquals } from '../../../../base/common/equals.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, ISettableObservable, ITransaction, ObservablePromise, derived, derivedHandleChanges, derivedOpts, disposableObservableValue, observableSignal, observableValue, recomputeInitiallyAndOnChange, subtransaction } from '../../../../base/common/observable.js'; -import { derivedDisposable } from '../../../../base/common/observableInternal/derived.js'; +import { IObservable, ISettableObservable, ITransaction, ObservablePromise, derived, derivedDisposable, derivedHandleChanges, derivedOpts, disposableObservableValue, observableSignal, observableValue, recomputeInitiallyAndOnChange, subtransaction } from '../../../../base/common/observable.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { IDiffProviderFactoryService } from '../../../browser/widget/diffEditor/diffProviderFactoryService.js'; diff --git a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts index f974af7800c..3da0aedd94a 100644 --- a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts +++ b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts @@ -6,9 +6,10 @@ import { h, svgElem } from '../../../../base/browser/dom.js'; import { DEFAULT_FONT_FAMILY } from '../../../../base/browser/fonts.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; -import { autorun, constObservable, derived, IObservable, ISettableObservable } from '../../../../base/common/observable.js'; -import { derivedWithSetter } from '../../../../base/common/observableInternal/derived.js'; -import './inlineEditsWidget.css'; +import { autorun, constObservable, derived, derivedWithSetter, IObservable, ISettableObservable } from '../../../../base/common/observable.js'; +import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorExtensionsRegistry } from '../../../browser/editorExtensions.js'; import { observableCodeEditor } from '../../../browser/observableCodeEditor.js'; @@ -24,9 +25,7 @@ import { TextModel } from '../../../common/model/textModel.js'; import { ContextMenuController } from '../../contextmenu/browser/contextmenu.js'; import { PlaceholderTextContribution } from '../../placeholderText/browser/placeholderTextContribution.js'; import { SuggestController } from '../../suggest/browser/suggestController.js'; -import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; -import { MenuId } from '../../../../platform/actions/common/actions.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import './inlineEditsWidget.css'; export class InlineEdit { constructor( diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts index 492d439ed23..b06224c3e9d 100644 --- a/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts @@ -6,9 +6,7 @@ import { h } from '../../../../base/browser/dom.js'; import { structuralEquals } from '../../../../base/common/equals.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { autorun, constObservable, derivedObservableWithCache, derivedOpts, IObservable, IReader } from '../../../../base/common/observable.js'; -import { DebugOwner } from '../../../../base/common/observableInternal/debugName.js'; -import { derivedWithStore } from '../../../../base/common/observableInternal/derived.js'; +import { autorun, constObservable, DebugOwner, derivedObservableWithCache, derivedOpts, derivedWithStore, IObservable, IReader } from '../../../../base/common/observable.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; diff --git a/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts b/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts index 6cf0e711027..ed312b7113e 100644 --- a/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts +++ b/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts @@ -8,8 +8,7 @@ import { getStructuralKey } from '../../../base/common/equals.js'; import { Event, IValueWithChangeEvent } from '../../../base/common/event.js'; import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; import { FileAccess } from '../../../base/common/network.js'; -import { derived, observableFromEvent } from '../../../base/common/observable.js'; -import { ValueWithChangeEventFromObservable } from '../../../base/common/observableInternal/utils.js'; +import { derived, observableFromEvent, ValueWithChangeEventFromObservable } from '../../../base/common/observable.js'; import { localize } from '../../../nls.js'; import { IAccessibilityService } from '../../accessibility/common/accessibility.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; diff --git a/src/vs/platform/observable/common/platformObservableUtils.ts b/src/vs/platform/observable/common/platformObservableUtils.ts index a75b0fcc3fb..225357badc9 100644 --- a/src/vs/platform/observable/common/platformObservableUtils.ts +++ b/src/vs/platform/observable/common/platformObservableUtils.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from '../../../base/common/lifecycle.js'; -import { autorunOpts, IObservable, IReader } from '../../../base/common/observable.js'; -import { observableFromEventOpts } from '../../../base/common/observableInternal/utils.js'; +import { autorunOpts, IObservable, IReader, observableFromEventOpts } from '../../../base/common/observable.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; import { ContextKeyValue, IContextKeyService, RawContextKey } from '../../contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts b/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts index 8f2e6afed40..28d6fe3edef 100644 --- a/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts +++ b/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts @@ -5,20 +5,19 @@ import { disposableTimeout } from '../../../../base/common/async.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { IReader, autorun, autorunWithStore, derived, observableFromEvent, observableFromPromise } from '../../../../base/common/observable.js'; -import { observableFromValueWithChangeEvent, observableSignalFromEvent, wasEventTriggeredRecently } from '../../../../base/common/observableInternal/utils.js'; +import { IReader, autorun, autorunWithStore, derived, observableFromEvent, observableFromPromise, observableFromValueWithChangeEvent, observableSignalFromEvent, wasEventTriggeredRecently } from '../../../../base/common/observable.js'; import { isDefined } from '../../../../base/common/types.js'; import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; import { Position } from '../../../../editor/common/core/position.js'; import { CursorChangeReason } from '../../../../editor/common/cursorEvents.js'; import { ITextModel } from '../../../../editor/common/model.js'; import { FoldingController } from '../../../../editor/contrib/folding/browser/folding.js'; -import { AccessibilitySignal, AccessibilityModality, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; +import { AccessibilityModality, AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { IDebugService } from '../../debug/common/debug.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { IDebugService } from '../../debug/common/debug.js'; export class EditorTextPropertySignalsContribution extends Disposable implements IWorkbenchContribution { private readonly _textProperties: TextProperty[] = [ diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index 6d882fdfca3..5a5b1a36679 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -11,8 +11,7 @@ import { IMarkdownString } from '../../../../base/common/htmlContent.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { revive } from '../../../../base/common/marshalling.js'; -import { IObservable } from '../../../../base/common/observable.js'; -import { observableValue } from '../../../../base/common/observableInternal/base.js'; +import { IObservable, observableValue } from '../../../../base/common/observable.js'; import { equalsIgnoreCase } from '../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 25632f11e6b..225089a5908 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -11,8 +11,7 @@ import { Disposable, DisposableStore, IDisposable, IReference } from '../../../. import { parse } from '../../../../base/common/marshalling.js'; import { Schemas } from '../../../../base/common/network.js'; import { deepClone } from '../../../../base/common/objects.js'; -import { ObservableLazyPromise, autorun, derived, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; -import { ValueWithChangeEventFromObservable, constObservable, mapObservableArrayCached, observableFromValueWithChangeEvent, recomputeInitiallyAndOnChange } from '../../../../base/common/observableInternal/utils.js'; +import { ObservableLazyPromise, ValueWithChangeEventFromObservable, autorun, constObservable, derived, mapObservableArrayCached, observableFromEvent, observableFromValueWithChangeEvent, observableValue, recomputeInitiallyAndOnChange } from '../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { isDefined, isObject } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; @@ -28,10 +27,10 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { IEditorConfiguration } from '../../../browser/parts/editor/textEditor.js'; import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, EditorInputWithOptions, GroupIdentifier, IEditorSerializer, IResourceMultiDiffEditorInput, IRevertOptions, ISaveOptions, IUntypedEditorInput } from '../../../common/editor.js'; import { EditorInput, IEditorCloseHandler } from '../../../common/editor/editorInput.js'; -import { MultiDiffEditorIcon } from './icons.contribution.js'; -import { IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from './multiDiffSourceResolverService.js'; import { IEditorResolverService, RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js'; import { ILanguageSupport, ITextFileEditorModel, ITextFileService } from '../../../services/textfile/common/textfiles.js'; +import { MultiDiffEditorIcon } from './icons.contribution.js'; +import { IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from './multiDiffSourceResolverService.js'; export class MultiDiffEditorInput extends EditorInput implements ILanguageSupport { public static fromResourceMultiDiffEditorInput(input: IResourceMultiDiffEditorInput, instantiationService: IInstantiationService): MultiDiffEditorInput { diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts index c907958ab68..cdd3aac6b29 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts @@ -4,18 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../base/common/lifecycle.js'; -import { observableFromEvent, waitForState } from '../../../../base/common/observable.js'; -import { ValueWithChangeEventFromObservable } from '../../../../base/common/observableInternal/utils.js'; +import { observableFromEvent, ValueWithChangeEventFromObservable, waitForState } from '../../../../base/common/observable.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; import { IMultiDiffEditorOptions } from '../../../../editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.js'; import { localize2 } from '../../../../nls.js'; import { Action2 } from '../../../../platform/actions/common/actions.js'; import { ContextKeyValue } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from './multiDiffSourceResolverService.js'; -import { ISCMRepository, ISCMResourceGroup, ISCMService } from '../../scm/common/scm.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IActivityService, ProgressBadge } from '../../../services/activity/common/activity.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { ISCMRepository, ISCMResourceGroup, ISCMService } from '../../scm/common/scm.js'; +import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from './multiDiffSourceResolverService.js'; export class ScmMultiDiffSourceResolver implements IMultiDiffSourceResolver { private static readonly _scheme = 'scm-multi-diff-source'; diff --git a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts index 0b9c0b6bcbf..19e3594791b 100644 --- a/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmHistoryViewPane.ts @@ -16,7 +16,7 @@ import { fromNow } from '../../../../base/common/date.js'; import { createMatches, FuzzyScore, IMatch } from '../../../../base/common/filters.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; -import { autorun, autorunWithStore, autorunWithStoreHandleChanges, derived, IObservable, observableValue, waitForState } from '../../../../base/common/observable.js'; +import { autorun, autorunWithStore, autorunWithStoreHandleChanges, derived, IObservable, observableValue, waitForState, constObservable, latestChangedValue, observableFromEvent, runOnChange, signalFromObservable } from '../../../../base/common/observable.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -48,7 +48,6 @@ import { ActionRunner, IAction, IActionRunner } from '../../../../base/common/ac import { delta, groupBy, tail } from '../../../../base/common/arrays.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { IProgressService } from '../../../../platform/progress/common/progress.js'; -import { constObservable, latestChangedValue, observableFromEvent, runOnChange, signalFromObservable } from '../../../../base/common/observableInternal/utils.js'; import { ContextKeys } from './scmViewPane.js'; import { IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IDropdownMenuActionViewItemOptions } from '../../../../base/browser/ui/dropdown/dropdownActionViewItem.js'; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index 7952dfbce0a..38c0119b7de 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -18,7 +18,7 @@ import { binarySearch } from '../../../../base/common/arrays.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { derivedObservableWithCache, latestChangedValue, observableFromEventOpts } from '../../../../base/common/observableInternal/utils.js'; +import { derivedObservableWithCache, latestChangedValue, observableFromEventOpts } from '../../../../base/common/observable.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { EditorResourceAccessor } from '../../../common/editor.js'; From 2a27ab1e48952c89ed66e537309ded7abc8132d4 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:07:54 +0200 Subject: [PATCH 37/45] Fix typo --- extensions/git/src/historyProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 3d115d8c116..02e3da449d8 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -275,7 +275,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec if (historyItemRefs.length === 0) { // TODO@lszomoru - log return undefined; - } else if (historyItemRefs.length === 1 && historyItemRefs[0] === this.currentHistoryItemRemoteRef?.id) { + } else if (historyItemRefs.length === 1 && historyItemRefs[0] === this.currentHistoryItemRef?.id) { // Remote if (this.currentHistoryItemRemoteRef) { const ancestor = await this.repository.getMergeBase(historyItemRefs[0], this.currentHistoryItemRemoteRef.id); From 0cb3ffae0f0855772c1cecf25cab92ffaa688889 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 10 Sep 2024 16:14:26 +0200 Subject: [PATCH 38/45] Observables - add previousValue to runOnChange* (#227725) --- src/vs/base/common/observableInternal/utils.ts | 13 ++++++++----- .../controller/inlineCompletionsController.ts | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/observableInternal/utils.ts b/src/vs/base/common/observableInternal/utils.ts index 47478220dae..2fb57d1a42a 100644 --- a/src/vs/base/common/observableInternal/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -625,7 +625,8 @@ export function derivedConstOnceDefined(owner: DebugOwner, fn: (reader: IRead type RemoveUndefined = T extends undefined ? never : T; -export function runOnChange(observable: IObservable, cb: (value: T, deltas: RemoveUndefined[]) => void): IDisposable { +export function runOnChange(observable: IObservable, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined[]) => void): IDisposable { + let _previousValue: T | undefined; return autorunWithStoreHandleChanges({ createEmptyChangeSummary: () => ({ deltas: [] as RemoveUndefined[], didChange: false }), handleChange: (context, changeSummary) => { @@ -640,17 +641,19 @@ export function runOnChange(observable: IObservable, cb: }, }, (reader, changeSummary) => { const value = observable.read(reader); + const previousValue = _previousValue; if (changeSummary.didChange) { - cb(value, changeSummary.deltas); + _previousValue = value; + cb(value, previousValue, changeSummary.deltas); } }); } -export function runOnChangeWithStore(observable: IObservable, cb: (value: T, deltas: RemoveUndefined[], store: DisposableStore) => void): IDisposable { +export function runOnChangeWithStore(observable: IObservable, cb: (value: T, previousValue: undefined | T, deltas: RemoveUndefined[], store: DisposableStore) => void): IDisposable { const store = new DisposableStore(); - const disposable = runOnChange(observable, (value, deltas) => { + const disposable = runOnChange(observable, (value, previousValue: undefined | T, deltas) => { store.clear(); - cb(value, deltas, store); + cb(value, previousValue, deltas, store); }); return { dispose() { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 424715a2aab..dcd8e176d5b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -155,7 +155,7 @@ export class InlineCompletionsController extends Disposable { } })); - this._register(runOnChange(this._editorObs.selections, (_value, changes) => { + this._register(runOnChange(this._editorObs.selections, (_value, _, changes) => { if (changes.some(e => e.reason === CursorChangeReason.Explicit || e.source === 'api')) { this.model.get()?.stop(); } @@ -203,7 +203,7 @@ export class InlineCompletionsController extends Disposable { this._playAccessibilitySignal.read(reader); currentInlineCompletionBySemanticId.read(reader); return {}; - }), async (_value, _deltas, store) => { + }), async (_value, _, _deltas, store) => { /** @description InlineCompletionsController.playAccessibilitySignalAndReadSuggestion */ const model = this.model.get(); const state = model?.state.get(); From eb80343e111cd8c01650e4830707c6af4143ccf5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 10 Sep 2024 16:17:58 +0200 Subject: [PATCH 39/45] Improve extensions UX for incompatible extensions (#228106) #227047 - Improve extensions UX for incompatible extensions - show a warning badge on the extensions activity icon - show a warning in the extensions view --- .../common/extensionsScannerService.ts | 4 +- .../extensions/common/extensionValidator.ts | 2 +- .../activitybar/media/activityaction.css | 17 --- .../browser/parts/compositeBarActions.ts | 37 ++++-- .../browser/parts/globalCompositeBar.ts | 3 +- .../browser/parts/media/paneCompositePart.css | 4 + .../extensions/browser/extensionsActions.ts | 8 +- .../extensions/browser/extensionsViewlet.ts | 109 ++++++++++++++---- .../extensions/browser/extensionsViews.ts | 28 ++++- .../extensions/browser/extensionsWidgets.ts | 9 +- .../browser/extensionsWorkbenchService.ts | 76 +++++++++++- .../browser/media/extensionsViewlet.css | 63 ++++++++-- .../contrib/extensions/common/extensions.ts | 15 ++- .../services/activity/common/activity.ts | 74 +++++++++++- 14 files changed, 361 insertions(+), 88 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 8f238a84029..aaa82639b30 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -689,8 +689,8 @@ class ExtensionsScanner extends Disposable { validate(extension: IRelaxedScannedExtension, input: ExtensionScannerInput): IRelaxedScannedExtension { let isValid = true; - const validateApiVersion = this.environmentService.isBuilt && this.extensionsEnabledWithApiProposalVersion.includes(extension.identifier.id.toLowerCase()); - const validations = validateExtensionManifest(input.productVersion, input.productDate, input.location, extension.manifest, extension.isBuiltin, validateApiVersion); + // const validateApiVersion = this.environmentService.isBuilt && this.extensionsEnabledWithApiProposalVersion.includes(extension.identifier.id.toLowerCase()); + const validations = validateExtensionManifest(input.productVersion, input.productDate, input.location, extension.manifest, extension.isBuiltin, true); for (const [severity, message] of validations) { if (severity === Severity.Error) { isValid = false; diff --git a/src/vs/platform/extensions/common/extensionValidator.ts b/src/vs/platform/extensions/common/extensionValidator.ts index d66cf72af3b..4a9d3295696 100644 --- a/src/vs/platform/extensions/common/extensionValidator.ts +++ b/src/vs/platform/extensions/common/extensionValidator.ts @@ -369,7 +369,7 @@ export function areApiProposalsCompatible(apiProposals: string[], arg1?: any): b continue; } if (existingProposal.version !== version) { - incompatibleNotices.push(nls.localize('apiProposalMismatch', "Extension is using an API proposal '{0}' that is not compatible with the current version of VS Code.", proposalName)); + incompatibleNotices.push(nls.localize('apiProposalMismatch', "This extension is using the API proposal '{0}' that is not compatible with the current version of VS Code.", proposalName)); } } notices?.push(...incompatibleNotices); diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index b40341d217c..22cd4083df3 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -169,23 +169,6 @@ text-align: center; } -.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .profile-badge .profile-icon-overlay { - position: absolute; - top: 27px; - right: 6px; - background-color: var(--vscode-activityBar-background); -} - -.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .profile-badge .profile-icon-overlay .codicon { - color: var(--vscode-activityBar-inactiveForeground); -} - -.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active .profile-badge .profile-icon-overlay .codicon, -.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .profile-badge .profile-icon-overlay .codicon, -.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .profile-badge .profile-icon-overlay .codicon { - color: var(--vscode-activityBar-foreground) !important; -} - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .profile-badge .profile-text-overlay { position: absolute; font-weight: 600; diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 61ddfcd61fb..15f6b06331c 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -10,7 +10,7 @@ import { ICommandService } from '../../../platform/commands/common/commands.js'; import { toDisposable, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js'; import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js'; import { IThemeService, IColorTheme } from '../../../platform/theme/common/themeService.js'; -import { NumberBadge, IBadge, IActivity, ProgressBadge } from '../../services/activity/common/activity.js'; +import { NumberBadge, IBadge, IActivity, ProgressBadge, IconBadge } from '../../services/activity/common/activity.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { DelayedDragHandler } from '../../../base/browser/dnd.js'; import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js'; @@ -154,7 +154,7 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { protected override readonly options: ICompositeBarActionViewItemOptions; private badgeContent: HTMLElement | undefined; - private readonly badgeDisposable = this._register(new MutableDisposable()); + private readonly badgeDisposable = this._register(new MutableDisposable()); private mouseUpTimeout: any; private keybindingLabel: string | undefined | null; @@ -214,9 +214,10 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { // Badge if (this.badgeContent) { - const badgeFg = colors.badgeForeground ?? theme.getColor(badgeForeground); - const badgeBg = colors.badgeBackground ?? theme.getColor(badgeBackground); - const contrastBorderColor = theme.getColor(contrastBorder); + const badgeStyles = this.getActivity()?.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); this.badgeContent.style.color = badgeFg ? badgeFg.toString() : ''; this.badgeContent.style.backgroundColor = badgeBg ? badgeBg.toString() : ''; @@ -285,15 +286,21 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { this.updateStyles(); } + private getActivity(): IActivity | undefined { + if (this._action instanceof CompositeBarAction) { + return this._action.activity; + } + return undefined; + } + protected updateActivity(): void { - const action = this.action; - if (!this.badge || !this.badgeContent || !(action instanceof CompositeBarAction)) { + if (!this.badge || !this.badgeContent || !(this._action instanceof CompositeBarAction)) { return; } - const activity = action.activity; + const activity = this.getActivity(); - this.badgeDisposable.clear(); + this.badgeDisposable.value = new DisposableStore(); clearNode(this.badgeContent); hide(this.badge); @@ -336,14 +343,24 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { } } + // Icon + else if (badge instanceof IconBadge) { + classes.push('icon-badge'); + const badgeContentClassess = ['icon-overlay', ...ThemeIcon.asClassNameArray(badge.icon)]; + this.badgeContent.classList.add(...badgeContentClassess); + this.badgeDisposable.value.add(toDisposable(() => this.badgeContent?.classList.remove(...badgeContentClassess))); + show(this.badge); + } + if (classes.length) { this.badge.classList.add(...classes); - this.badgeDisposable.value = toDisposable(() => this.badge.classList.remove(...classes)); + this.badgeDisposable.value.add(toDisposable(() => this.badge.classList.remove(...classes))); } } this.updateTitle(); + this.updateStyles(); } protected override updateLabel(): void { diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index 34f790d6107..8c96ab3e461 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -602,8 +602,7 @@ export class GlobalActivityActionViewItem extends AbstractGlobalActivityActionVi } show(this.profileBadge); - this.profileBadgeContent.classList.toggle('profile-text-overlay', true); - this.profileBadgeContent.classList.toggle('profile-icon-overlay', false); + this.profileBadgeContent.classList.add('profile-text-overlay'); this.profileBadgeContent.textContent = this.userDataProfileService.currentProfile.name.substring(0, 2).toUpperCase(); } diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index 52f116455cf..1f2bd9ee9d5 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -205,6 +205,10 @@ position: relative; } +.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .icon-badge .badge-content { + padding: 3px; +} + .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact, .monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact { position: absolute; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 974c01deb12..0390170863a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -2653,13 +2653,17 @@ export class ExtensionStatusAction extends ExtensionAction { // Extension is disabled by its dependency if (this.extension.enablementState === EnablementState.DisabledByExtensionDependency) { - this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('extension disabled because of dependency', "This extension has been disabled because it depends on an extension that is disabled.")) }, true); + this.updateStatus({ + icon: warningIcon, + message: new MarkdownString(localize('extension disabled because of dependency', "This extension depends on an extension that is disabled.")) + .appendMarkdown(` [${localize('dependencies', "Show Dependencies")}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.identifier.id, ExtensionEditorTab.Dependencies]))}`)})`) + }, true); return; } if (!this.extension.local.isValid) { const errors = this.extension.local.validations.filter(([severity]) => severity === Severity.Error).map(([, message]) => message); - this.updateStatus({ icon: errorIcon, message: new MarkdownString(errors.join(' ').trim()) }, true); + this.updateStatus({ icon: warningIcon, message: new MarkdownString(errors.join(' ').trim()) }, true); return; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index e99f31a59ec..f6f9270f85a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -9,10 +9,10 @@ import { timeout, Delayer, Promises } from '../../../../base/common/async.js'; import { isCancellationError } from '../../../../base/common/errors.js'; import { createErrorWithActions } from '../../../../base/common/errorMessage.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { Event } from '../../../../base/common/event.js'; import { Action } from '../../../../base/common/actions.js'; -import { append, $, Dimension, hide, show, DragAndDropObserver, trackFocus } from '../../../../base/browser/dom.js'; +import { append, $, Dimension, hide, show, DragAndDropObserver, trackFocus, addDisposableListener, EventType, clearNode } from '../../../../base/browser/dom.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; @@ -25,7 +25,7 @@ import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, Reco import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import Severity from '../../../../base/common/severity.js'; -import { IActivityService, NumberBadge } from '../../../services/activity/common/activity.js'; +import { IActivityService, IBadge, NumberBadge, WarningBadge } from '../../../services/activity/common/activity.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewDescriptorService, IAddedViewDescriptorRef, ViewContainerLocation } from '../../../common/views.js'; @@ -64,6 +64,9 @@ import { ILocalizedString } from '../../../../platform/action/common/action.js'; import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { KeyCode } from '../../../../base/common/keyCodes.js'; export const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); export const ExtensionsSortByContext = new RawContextKey('extensionsSortByValue', ''); @@ -494,7 +497,9 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE private searchDelayer: Delayer; private root: HTMLElement | undefined; + private header: HTMLElement | undefined; private searchBox: SuggestEnabledInput | undefined; + private notificationContainer: HTMLElement | undefined; private readonly searchViewletState: MementoObject; constructor( @@ -557,12 +562,12 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE overlay.style.backgroundColor = overlayBackgroundColor; hide(overlay); - const header = append(this.root, $('.header')); + this.header = append(this.root, $('.header')); const placeholder = localize('searchExtensions', "Search Extensions in Marketplace"); const searchValue = this.searchViewletState['query.value'] ? this.searchViewletState['query.value'] : ''; - const searchContainer = append(header, $('.extensions-search-container')); + const searchContainer = append(this.header, $('.extensions-search-container')); this.searchBox = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${VIEWLET_ID}.searchbox`, searchContainer, { triggerCharacters: ['@'], @@ -575,6 +580,10 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE provideResults: (query: string) => Query.suggestions(query) }, placeholder, 'extensions:searchinput', { placeholderText: placeholder, value: searchValue })); + this.notificationContainer = append(this.header, $('.notification-container.hidden', { 'tabindex': '0' })); + this.renderNotificaiton(); + this._register(this.extensionsWorkbenchService.onDidChangeExtensionsNotification(() => this.renderNotificaiton())); + this.updateInstalledExtensionsContexts(); if (this.searchBox.getValue()) { this.triggerSearch(); @@ -657,13 +666,18 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.searchBox?.focus(); } + private _dimension: Dimension | undefined; override layout(dimension: Dimension): void { + this._dimension = dimension; if (this.root) { this.root.classList.toggle('narrow', dimension.width <= 250); this.root.classList.toggle('mini', dimension.width <= 200); } this.searchBox?.layout(new Dimension(dimension.width - 34 - /*padding*/8 - (24 * 2), 20)); - super.layout(new Dimension(dimension.width, dimension.height - 41)); + const searchBoxHeight = 20 + 21 /*margin*/; + const headerHeight = this.header && !!this.notificationContainer?.childNodes.length ? this.notificationContainer.clientHeight + searchBoxHeight + 10 /*margin*/ : searchBoxHeight; + this.header!.style.height = `${headerHeight}px`; + super.layout(new Dimension(dimension.width, dimension.height - headerHeight)); } override getOptimalWidth(): number { @@ -684,6 +698,46 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE } } + private readonly notificationDisposables = this._register(new MutableDisposable()); + private renderNotificaiton(): void { + if (!this.notificationContainer) { + return; + } + + clearNode(this.notificationContainer); + this.notificationDisposables.value = new DisposableStore(); + const status = this.extensionsWorkbenchService.getExtensionsNotification(); + if (status && !this.searchMarketplaceExtensionsContextKey.get()) { + this.notificationContainer.setAttribute('aria-label', status.message); + this.notificationContainer.classList.remove('hidden'); + const messageContainer = append(this.notificationContainer, $('.message-container')); + append(messageContainer, $('span')).className = SeverityIcon.className(status.severity); + append(messageContainer, $('span.message', undefined, status.message)); + const showAction = append(messageContainer, + $('span.message-text-action', { + 'tabindex': '0', + 'role': 'button', + 'aria-label': `${status.message}. ${localize('click show', "Click to Show")}` + }, localize('show', "Show"))); + const showExtensions = () => this.search(status.extensions.map(extension => `@id:${extension.identifier.id}`).join(' ')); + this.notificationDisposables.value.add(addDisposableListener(showAction, EventType.CLICK, () => showExtensions())); + this.notificationDisposables.value.add(addDisposableListener(showAction, EventType.KEY_DOWN, (e: KeyboardEvent) => { + const standardKeyboardEvent = new StandardKeyboardEvent(e); + if (standardKeyboardEvent.keyCode === KeyCode.Enter || standardKeyboardEvent.keyCode === KeyCode.Space) { + showExtensions(); + } + standardKeyboardEvent.stopPropagation(); + })); + } else { + this.notificationContainer.removeAttribute('aria-label'); + this.notificationContainer.classList.add('hidden'); + } + + if (this._dimension) { + this.layout(this._dimension); + } + } + private async updateInstalledExtensionsContexts(): Promise { const result = await this.extensionsWorkbenchService.queryLocal(); this.hasInstalledExtensionsContextKey.set(result.some(r => !r.isBuiltin)); @@ -737,6 +791,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.defaultViewsContextKey.set(!value || ExtensionsListView.isSortInstalledExtensionsQuery(value)); }); + this.renderNotificaiton(); + return this.progress(Promise.all(this.panes.map(view => (view).show(this.normalizedQuery(), refresh) .then(model => this.alertSearchResult(model.length, view.id)) @@ -851,27 +907,40 @@ export class StatusUpdater extends Disposable implements IWorkbenchContribution ) { super(); this.onServiceChange(); - this._register(Event.debounce(extensionsWorkbenchService.onChange, () => undefined, 100, undefined, undefined, undefined, this._store)(this.onServiceChange, this)); + this._register(Event.any(Event.debounce(extensionsWorkbenchService.onChange, () => undefined, 100, undefined, undefined, undefined, this._store), extensionsWorkbenchService.onDidChangeExtensionsNotification)(this.onServiceChange, this)); } private onServiceChange(): void { this.badgeHandle.clear(); + let badge: IBadge | undefined; - const actionRequired = this.configurationService.getValue(AutoRestartConfigurationKey) === true ? [] : this.extensionsWorkbenchService.installed.filter(e => e.runtimeState !== undefined); - const outdated = this.extensionsWorkbenchService.outdated.reduce((r, e) => r + (this.extensionEnablementService.isEnabled(e.local!) && !actionRequired.includes(e) ? 1 : 0), 0); - const newBadgeNumber = outdated + actionRequired.length; - if (newBadgeNumber > 0) { - let msg = ''; - if (outdated) { - msg += outdated === 1 ? localize('extensionToUpdate', '{0} requires update', outdated) : localize('extensionsToUpdate', '{0} require update', outdated); + const extensionsNotification = this.extensionsWorkbenchService.getExtensionsNotification(); + if (extensionsNotification) { + if (extensionsNotification.severity === Severity.Warning) { + badge = new WarningBadge(() => extensionsNotification.message); } - if (outdated > 0 && actionRequired.length > 0) { - msg += ', '; + } + + else { + const actionRequired = this.configurationService.getValue(AutoRestartConfigurationKey) === true ? [] : this.extensionsWorkbenchService.installed.filter(e => e.runtimeState !== undefined); + const outdated = this.extensionsWorkbenchService.outdated.reduce((r, e) => r + (this.extensionEnablementService.isEnabled(e.local!) && !actionRequired.includes(e) ? 1 : 0), 0); + const newBadgeNumber = outdated + actionRequired.length; + if (newBadgeNumber > 0) { + let msg = ''; + if (outdated) { + msg += outdated === 1 ? localize('extensionToUpdate', '{0} requires update', outdated) : localize('extensionsToUpdate', '{0} require update', outdated); + } + if (outdated > 0 && actionRequired.length > 0) { + msg += ', '; + } + if (actionRequired.length) { + msg += actionRequired.length === 1 ? localize('extensionToReload', '{0} requires restart', actionRequired.length) : localize('extensionsToReload', '{0} require restart', actionRequired.length); + } + badge = new NumberBadge(newBadgeNumber, () => msg); } - if (actionRequired.length) { - msg += actionRequired.length === 1 ? localize('extensionToReload', '{0} requires restart', actionRequired.length) : localize('extensionsToReload', '{0} require restart', actionRequired.length); - } - const badge = new NumberBadge(newBadgeNumber, () => msg); + } + + if (badge) { this.badgeHandle.value = this.activityService.showViewContainerActivity(VIEWLET_ID, { badge }); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index a36f9be88bb..32bb10a91bc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -552,11 +552,24 @@ export class ExtensionsListView extends ViewPane { return isE1Running ? -1 : 1; }; + const incompatible: IExtension[] = []; + const missingDeps: IExtension[] = []; + const deprecated: IExtension[] = []; const outdated: IExtension[] = []; const actionRequired: IExtension[] = []; const noActionRequired: IExtension[] = []; - result.forEach(e => { - if (e.outdated) { + + for (const e of result) { + if (e.enablementState === EnablementState.DisabledByInvalidExtension) { + incompatible.push(e); + } + else if (e.enablementState === EnablementState.DisabledByExtensionDependency) { + missingDeps.push(e); + } + else if (e.deprecationInfo) { + deprecated.push(e); + } + else if (e.outdated) { outdated.push(e); } else if (e.runtimeState) { @@ -565,9 +578,16 @@ export class ExtensionsListView extends ViewPane { else { noActionRequired.push(e); } - }); + } - result = [...outdated.sort(defaultSort), ...actionRequired.sort(defaultSort), ...noActionRequired.sort(defaultSort)]; + result = [ + ...incompatible.sort(defaultSort), + ...missingDeps.sort(defaultSort), + ...deprecated.sort(defaultSort), + ...outdated.sort(defaultSort), + ...actionRequired.sort(defaultSort), + ...noActionRequired.sort(defaultSort) + ]; } return result; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 564e080f583..417c7bba2a7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -10,7 +10,7 @@ import { IExtension, IExtensionsWorkbenchService, IExtensionContainer, Extension import { append, $, reset, addDisposableListener, EventType, finalHandler } from '../../../../base/browser/dom.js'; import * as platform from '../../../../base/common/platform.js'; import { localize } from '../../../../nls.js'; -import { EnablementState, IExtensionManagementServerService } from '../../../services/extensionManagement/common/extensionManagement.js'; +import { IExtensionManagementServerService } from '../../../services/extensionManagement/common/extensionManagement.js'; import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { extensionButtonProminentBackground, ExtensionStatusAction } from './extensionsActions.js'; @@ -501,7 +501,7 @@ export class ExtensionActivationStatusWidget extends ExtensionWidget { return; } - const extensionStatus = this.extensionsWorkbenchService.getExtensionStatus(this.extension); + const extensionStatus = this.extensionsWorkbenchService.getExtensionRuntimeStatus(this.extension); if (!extensionStatus || !extensionStatus.activationTimes) { return; } @@ -647,7 +647,7 @@ export class ExtensionHoverWidget extends ExtensionWidget { } const preReleaseMessage = ExtensionHoverWidget.getPreReleaseMessage(this.extension); - const extensionRuntimeStatus = this.extensionsWorkbenchService.getExtensionStatus(this.extension); + const extensionRuntimeStatus = this.extensionsWorkbenchService.getExtensionRuntimeStatus(this.extension); const extensionStatus = this.extensionStatusAction.status; const runtimeState = this.extension.runtimeState; const recommendationMessage = this.getRecommendationMessage(this.extension); @@ -683,9 +683,6 @@ export class ExtensionHoverWidget extends ExtensionWidget { markdown.appendMarkdown(`$(${status.icon.id}) `); } markdown.appendMarkdown(status.message.value); - if (this.extension.enablementState === EnablementState.DisabledByExtensionDependency && this.extension.local) { - markdown.appendMarkdown(` [${localize('dependencies', "Show Dependencies")}](${URI.parse(`command:extension.open?${encodeURIComponent(JSON.stringify([this.extension.identifier.id, ExtensionEditorTab.Dependencies]))}`)})`); - } markdown.appendText(`\n`); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index d3deb35f309..6e966272405 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -25,7 +25,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IHostService } from '../../../services/host/browser/host.js'; import { URI } from '../../../../base/common/uri.js'; -import { IExtension, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey, HasOutdatedExtensionsContext, AutoUpdateConfigurationValue, InstallExtensionOptions, ExtensionRuntimeState, ExtensionRuntimeActionType, AutoRestartConfigurationKey, VIEWLET_ID, IExtensionsViewPaneContainer } from '../common/extensions.js'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey, HasOutdatedExtensionsContext, AutoUpdateConfigurationValue, InstallExtensionOptions, ExtensionRuntimeState, ExtensionRuntimeActionType, AutoRestartConfigurationKey, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionsNotification } from '../common/extensions.js'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; import { IURLService, IURLHandler, IOpenURLOptions } from '../../../../platform/url/common/url.js'; import { ExtensionsInput, IExtensionEditorOptions } from '../common/extensionsInput.js'; @@ -45,7 +45,7 @@ import { IUserDataAutoSyncService, IUserDataSyncEnablementService, SyncResource import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { isBoolean, isDefined, isString, isUndefined } from '../../../../base/common/types.js'; import { IExtensionManifestPropertiesService } from '../../../services/extensions/common/extensionManifestPropertiesService.js'; -import { IExtensionService, IExtensionsStatus, toExtension, toExtensionDescription } from '../../../services/extensions/common/extensions.js'; +import { IExtensionService, IExtensionsStatus as IExtensionRuntimeStatus, toExtension, toExtensionDescription } from '../../../services/extensions/common/extensions.js'; import { isWeb, language } from '../../../../base/common/platform.js'; import { getLocale } from '../../../../platform/languagePacks/common/languagePacks.js'; import { ILocaleService } from '../../../services/localization/common/locale.js'; @@ -55,7 +55,7 @@ import { IUserDataProfileService } from '../../../services/userDataProfile/commo import { mainWindow } from '../../../../base/browser/window.js'; import { IDialogService, IPromptButton } from '../../../../platform/dialogs/common/dialogs.js'; import { IUpdateService, StateType } from '../../../../platform/update/common/update.js'; -import { isEngineValid } from '../../../../platform/extensions/common/extensionValidator.js'; +import { areApiProposalsCompatible, isEngineValid } from '../../../../platform/extensions/common/extensionValidator.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { ShowCurrentReleaseNotesActionId } from '../../update/common/update.js'; @@ -904,9 +904,14 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private updatesCheckDelayer: ThrottledDelayer; private autoUpdateDelayer: ThrottledDelayer; - private readonly _onChange: Emitter = new Emitter(); + private readonly _onChange = this._register(new Emitter()); get onChange(): Event { return this._onChange.event; } + private extensionsNotification: IExtensionsNotification | undefined; + private dismissedNotifications: string[] = []; + private readonly _onDidChangeExtensionsNotification = new Emitter(); + readonly onDidChangeExtensionsNotification = this._onDidChangeExtensionsNotification.event; + private readonly _onReset = new Emitter(); get onReset() { return this._onReset.event; } @@ -1033,11 +1038,16 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (this._store.isDisposed) { return; } + this.initializeAutoUpdate(); + this.updateExtensionsNotificaiton(); this.reportInstalledExtensionsTelemetry(); - this._register(Event.debounce(this.onChange, () => undefined, 100)(() => this.reportProgressFromOtherSources())); this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, EXTENSIONS_AUTO_UPDATE_KEY, this._store)(e => this.onDidSelectedExtensionToAutoUpdateValueChange())); this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, EXTENSIONS_DONOT_AUTO_UPDATE_KEY, this._store)(e => this.onDidSelectedExtensionToAutoUpdateValueChange())); + this._register(Event.debounce(this.onChange, () => undefined, 100)(() => { + this.updateExtensionsNotificaiton(); + this.reportProgressFromOtherSources(); + })); } private initializeAutoUpdate(): void { @@ -1329,6 +1339,60 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension ?? this.instantiationService.createInstance(Extension, ext => this.getExtensionState(ext), ext => this.getRuntimeState(ext), undefined, undefined, undefined, { resourceExtension, isWorkspaceScoped })); } + private updateExtensionsNotificaiton(): void { + let extensionsNotification: IExtensionsNotification | undefined; + + let message = ''; + const severity = Severity.Warning; + const extensions: IExtension[] = []; + + extensions.push(...this.local.filter(e => e.enablementState === EnablementState.DisabledByInvalidExtension)); + if (extensions.length) { + if (extensions.some(e => e.local && + (!isEngineValid(e.local.manifest.engines.vscode, this.productService.version, this.productService.date) || areApiProposalsCompatible([...e.local.manifest.enabledApiProposals ?? []])) + )) { + message = nls.localize('incompatibleExtensions', "Some extensions are disabled due to version incompatibility. Review and update them."); + } else { + message = nls.localize('invalidExtensions', "You have invalid extensions installed. Review them."); + } + } + + else { + extensions.push(...this.local.filter(e => e.enablementState === EnablementState.DisabledByExtensionDependency)); + if (extensions.length) { + message = nls.localize('missingDependencies', "Some extensions are disabled due to missing dependencies. Review them."); + } + + else { + extensions.push(...this.local.filter(e => !!e.deprecationInfo)); + if (extensions.length) { + message = nls.localize('deprecated extensions', "You have deprecated extensions installed. Review them and migrate to alternatives."); + } + } + } + + if (extensions.length && !this.dismissedNotifications.includes(message)) { + extensionsNotification = { + message, + severity, + extensions, + dismiss: () => { + this.dismissedNotifications.push(message); + this.updateExtensionsNotificaiton(); + }, + }; + } + + if (this.extensionsNotification?.message !== extensionsNotification?.message) { + this.extensionsNotification = extensionsNotification; + this._onDidChangeExtensionsNotification.fire(this.extensionsNotification); + } + } + + getExtensionsNotification(): IExtensionsNotification | undefined { + return this.extensionsNotification; + } + private resolveQueryText(text: string): string { text = text.replace(/@web/g, `tag:"${WEB_EXTENSION_TAG}"`); @@ -1399,7 +1463,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } } - getExtensionStatus(extension: IExtension): IExtensionsStatus | undefined { + getExtensionRuntimeStatus(extension: IExtension): IExtensionRuntimeStatus | undefined { const extensionsStatus = this.extensionService.getExtensionsStatus(); for (const id of Object.keys(extensionsStatus)) { if (areSameExtensions({ id }, extension.identifier)) { diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css index a4514bd07c0..b0141e1c4fa 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsViewlet.css @@ -8,6 +8,11 @@ height: 100%; } +.extensions-viewlet .hidden { + display: none; + visibility: hidden; +} + .extensions-viewlet > .overlay { position: absolute; top: 0; @@ -47,6 +52,44 @@ height: 100%; } +.extensions-viewlet > .header > .notification-container { + margin-top: 10px; + display: flex; + justify-content: space-between; +} + +.extensions-viewlet > .header .notification-container .message-container { + padding-left: 4px; +} + +.extensions-viewlet > .header .notification-container .message-container .codicon { + vertical-align: text-top; + padding-right: 5px; +} + +.extensions-viewlet .notification-container .message-text-action { + cursor: pointer; + margin-left: 5px; + color: var(--vscode-textLink-foreground); + text-decoration: underline; +} + +.extensions-viewlet .notification-container .message-text-action:hover, +.extensions-viewlet .notification-container .message-text-action:active { + color: var(--vscode-textLink-activeForeground); +} + +.extensions-viewlet .notification-container .message-action { + cursor: pointer; + padding: 2px; + border-radius: 5px; + height: 16px; +} + +.extensions-viewlet .notification-container .message-action:hover { + background-color: var(--vscode-toolbar-hoverBackground); + outline: 1px dashed var(--vscode-toolbar-hoverOutline); +} .extensions-viewlet > .extensions { height: calc(100% - 41px); @@ -70,12 +113,6 @@ display: none; } -.extensions-viewlet > .extensions .extensions-list.hidden, -.extensions-viewlet > .extensions .message-container.hidden { - display: none; - visibility: hidden; -} - .extensions-viewlet > .extensions .panel-header { padding-right: 12px; } @@ -151,8 +188,14 @@ opacity: 0.5; } -.extensions-badge.progress-badge > .badge-content { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMiAyIDE0IDE0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDIgMiAxNCAxNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTkgMTZjLTMuODYgMC03LTMuMTQtNy03czMuMTQtNyA3LTdjMy44NTkgMCA3IDMuMTQxIDcgN3MtMy4xNDEgNy03IDd6bTAtMTIuNmMtMy4wODggMC01LjYgMi41MTMtNS42IDUuNnMyLjUxMiA1LjYgNS42IDUuNiA1LjYtMi41MTIgNS42LTUuNi0yLjUxMi01LjYtNS42LTUuNnptMy44NiA3LjFsLTMuMTYtMS44OTZ2LTMuODA0aC0xLjR2NC41OTZsMy44NCAyLjMwNS43Mi0xLjIwMXoiLz48L3N2Zz4="); - background-position: center center; - background-repeat: no-repeat; +.extensions-viewlet .codicon-error::before { + color: var(--vscode-editorError-foreground); +} + +.extensions-viewlet .codicon-warning::before { + color: var(--vscode-editorWarning-foreground); +} + +.extensions-viewlet .codicon-info::before { + color: var(--vscode-editorInfo-foreground); } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 4c5e1efa44c..9cae86e743c 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -15,10 +15,11 @@ import { IExtensionManifest, ExtensionType } from '../../../../platform/extensio import { URI } from '../../../../base/common/uri.js'; import { IView, IViewPaneContainer } from '../../../common/views.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { IExtensionsStatus } from '../../../services/extensions/common/extensions.js'; +import { IExtensionsStatus as IExtensionRuntimeStatus } from '../../../services/extensions/common/extensions.js'; import { IExtensionEditorOptions } from './extensionsInput.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { ProgressLocation } from '../../../../platform/progress/common/progress.js'; +import { Severity } from '../../../../platform/notification/common/notification.js'; export const VIEWLET_ID = 'workbench.view.extensions'; @@ -110,6 +111,13 @@ export interface InstallExtensionOptions extends InstallOptions { enable?: boolean; } +export interface IExtensionsNotification { + readonly message: string; + readonly severity: Severity; + readonly extensions: IExtension[]; + dismiss(): void; +} + export interface IExtensionsWorkbenchService { readonly _serviceBrand: undefined; readonly onChange: Event; @@ -144,10 +152,13 @@ export interface IExtensionsWorkbenchService { openSearch(searchValue: string, focus?: boolean): Promise; getAutoUpdateValue(): AutoUpdateConfigurationValue; checkForUpdates(): Promise; - getExtensionStatus(extension: IExtension): IExtensionsStatus | undefined; + getExtensionRuntimeStatus(extension: IExtension): IExtensionRuntimeStatus | undefined; updateAll(): Promise; updateRunningExtensions(): Promise; + onDidChangeExtensionsNotification: Event; + getExtensionsNotification(): IExtensionsNotification | undefined; + // Sync APIs isExtensionIgnoredToSync(extension: IExtension): boolean; toggleExtensionIgnoredToSync(extension: IExtension): Promise; diff --git a/src/vs/workbench/services/activity/common/activity.ts b/src/vs/workbench/services/activity/common/activity.ts index 7599f94db2f..481a4648a4f 100644 --- a/src/vs/workbench/services/activity/common/activity.ts +++ b/src/vs/workbench/services/activity/common/activity.ts @@ -8,6 +8,11 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta import { ThemeIcon } from '../../../../base/common/themables.js'; import { Event } from '../../../../base/common/event.js'; import { ViewContainer } from '../../../common/views.js'; +import { IColorTheme } from '../../../../platform/theme/common/themeService.js'; +import { Color } from '../../../../base/common/color.js'; +import { registerColor } from '../../../../platform/theme/common/colorUtils.js'; +import { localize } from '../../../../nls.js'; +import { Codicon } from '../../../../base/common/codicons.js'; export interface IActivity { readonly badge: IBadge; @@ -58,23 +63,36 @@ export interface IActivityService { export interface IBadge { getDescription(): string; + getColors(theme: IColorTheme): IBadgeStyles | undefined; +} + +export interface IBadgeStyles { + readonly badgeBackground: Color | undefined; + readonly badgeForeground: Color | undefined; + readonly badgeBorder: Color | undefined; } class BaseBadge implements IBadge { - constructor(readonly descriptorFn: (arg: any) => string) { - this.descriptorFn = descriptorFn; + constructor( + protected readonly descriptorFn: (arg: any) => string, + private readonly stylesFn: ((theme: IColorTheme) => IBadgeStyles | undefined) | undefined, + ) { } getDescription(): string { return this.descriptorFn(null); } + + getColors(theme: IColorTheme): IBadgeStyles | undefined { + return this.stylesFn?.(theme); + } } export class NumberBadge extends BaseBadge { constructor(readonly number: number, descriptorFn: (num: number) => string) { - super(descriptorFn); + super(descriptorFn, undefined); this.number = number; } @@ -85,9 +103,53 @@ export class NumberBadge extends BaseBadge { } export class IconBadge extends BaseBadge { - constructor(readonly icon: ThemeIcon, descriptorFn: () => string) { - super(descriptorFn); + constructor( + readonly icon: ThemeIcon, + descriptorFn: () => string, + stylesFn?: (theme: IColorTheme) => IBadgeStyles | undefined, + ) { + super(descriptorFn, stylesFn); } } -export class ProgressBadge extends BaseBadge { } +export class ProgressBadge extends BaseBadge { + constructor(descriptorFn: () => string) { + super(descriptorFn, undefined); + } +} + +export class WarningBadge extends IconBadge { + constructor(descriptorFn: () => string) { + super(Codicon.warning, descriptorFn, (theme: IColorTheme) => ({ + badgeBackground: theme.getColor(activityWarningBadgeBackground), + badgeForeground: theme.getColor(activityWarningBadgeForeground), + badgeBorder: undefined, + })); + } +} + +export class ErrorBadge extends IconBadge { + constructor(descriptorFn: () => string) { + super(Codicon.error, descriptorFn, (theme: IColorTheme) => ({ + badgeBackground: theme.getColor(activityErrorBadgeBackground), + badgeForeground: theme.getColor(activityErrorBadgeForeground), + badgeBorder: undefined, + })); + } +} + +const activityWarningBadgeForeground = registerColor('activityWarningBadge.foreground', + { dark: Color.black.lighten(0.2), light: Color.white, hcDark: null, hcLight: null }, + localize('activityWarningBadge.foreground', 'Foreground color of the warning activity badge')); + +const activityWarningBadgeBackground = registerColor('activityWarningBadge.background', + { dark: '#CCA700', light: '#BF8803', hcDark: null, hcLight: null }, + localize('activityWarningBadge.background', 'Background color of the warning activity badge')); + +const activityErrorBadgeForeground = registerColor('activityErrorBadge.foreground', + { dark: Color.black.lighten(0.2), light: Color.white, hcDark: null, hcLight: null }, + localize('activityErrorBadge.foreground', 'Foreground color of the error activity badge')); + +const activityErrorBadgeBackground = registerColor('activityErrorBadge.background', + { dark: '#F14C4C', light: '#E51400', hcDark: null, hcLight: null }, + localize('activityErrorBadge.background', 'Background color of the error activity badge')); From f12bee2c50b7f3e6de3145ff13919cff66bf32d8 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 10 Sep 2024 17:46:33 +0200 Subject: [PATCH 40/45] apply editor: do not fallback to insert block (#228116) --- .../browser/actions/chatCodeblockActions.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index e990dadc0ef..fd64f264501 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -216,11 +216,14 @@ abstract class InsertCodeBlockAction extends ChatCodeBlockAction { this.notifyUserAction(chatService, context); } - protected async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise { + protected async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise { const activeModel = codeEditor.getModel(); const range = codeEditor.getSelection() ?? new Range(activeModel.getLineCount(), 1, activeModel.getLineCount(), 1); const text = reindent(codeBlockActionContext.code, activeModel, range.startLineNumber); - return { edits: [new ResourceTextEdit(activeModel.uri, { range, text })] }; + if (text !== undefined) { + return { edits: [new ResourceTextEdit(activeModel.uri, { range, text })] }; + } + return undefined; } protected get showPreview() { @@ -234,6 +237,9 @@ abstract class InsertCodeBlockAction extends ChatCodeBlockAction { const result = await this.computeEdits(accessor, codeEditor, codeBlockActionContext); this.notifyUserAction(chatService, codeBlockActionContext, result); + if (!result) { + return; + } if (this.showPreview) { const showWithPreview = await this.applyWithInlinePreview(codeEditorService, result.edits, codeEditor); @@ -295,10 +301,10 @@ abstract class InsertCodeBlockAction extends ChatCodeBlockAction { } -function reindent(codeBlockContent: string, model: ITextModel, seletionStartLine: number) { +function reindent(codeBlockContent: string, model: ITextModel, seletionStartLine: number): string | undefined { const newContent = strings.splitLines(codeBlockContent); if (newContent.length === 0) { - return codeBlockContent; + return undefined; } const formattingOptions = model.getFormattingOptions(); @@ -316,7 +322,7 @@ function reindent(codeBlockContent: string, model: ITextModel, seletionStartLine if (newContentIndentLevel === Number.MAX_VALUE || newContentIndentLevel === codeIndentLevel) { // all lines are empty or the indent is already correct - return codeBlockContent; + return undefined; } const newLines = []; for (let i = 0; i < newContent.length; i++) { @@ -483,7 +489,7 @@ export function registerChatCodeBlockActions() { }); } - protected override async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise { + protected override async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise { const progressService = accessor.get(IProgressService); const notificationService = accessor.get(INotificationService); @@ -543,15 +549,14 @@ export function registerChatCodeBlockActions() { } } catch (e) { notificationService.notify({ severity: Severity.Error, message: localize('applyCodeBlock.error', "Failed to apply code block: {0}", e.message) }); - } finally { cancellationTokenSource.dispose(); } } - // fall back to inserting the code block as is - return super.computeEdits(accessor, codeEditor, codeBlockActionContext); + return undefined; } + protected override get showPreview() { return true; } From 39684940e825a198c828064ab625650b96bb25d0 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 10 Sep 2024 18:40:10 +0200 Subject: [PATCH 41/45] Cleanup debug model. (#223673) --- src/vs/base/common/collections.ts | 2 +- src/vs/base/common/event.ts | 29 ++++++++++++++++++- .../contrib/debug/common/debugModel.ts | 21 +++++--------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index d0df190c75b..a47e629160e 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -32,7 +32,7 @@ export function groupBy(data: V[], groupF return result; } -export function diffSets(before: Set, after: Set): { removed: T[]; added: T[] } { +export function diffSets(before: ReadonlySet, after: ReadonlySet): { removed: T[]; added: T[] } { const removed: T[] = []; const added: T[] = []; for (const element of before) { diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index e40c0caace9..915f481fba6 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from './cancellation.js'; +import { diffSets } from './collections.js'; import { onUnexpectedError } from './errors.js'; import { createSingleCallFunction } from './functional.js'; import { combinedDisposable, Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from './lifecycle.js'; @@ -12,7 +13,6 @@ import { IObservable, IObserver } from './observable.js'; import { StopWatch } from './stopwatch.js'; import { MicrotaskDelay } from './symbols.js'; - // ----------------------------------------------------------------------------------------------------------------------- // Uncomment the next line to print warnings whenever a listener is GC'ed without having been disposed. This is a LEAK. // ----------------------------------------------------------------------------------------------------------------------- @@ -1791,3 +1791,30 @@ class ConstValueWithChangeEvent implements IValueWithChangeEvent { constructor(readonly value: T) { } } + +/** + * @param handleItem Is called for each item in the set (but only the first time the item is seen in the set). + * The returned disposable is disposed if the item is no longer in the set. + */ +export function trackSetChanges(getData: () => ReadonlySet, onDidChangeData: Event, handleItem: (d: T) => IDisposable): IDisposable { + const map = new DisposableMap(); + let oldData = new Set(getData()); + for (const d of oldData) { + map.set(d, handleItem(d)); + } + + const store = new DisposableStore(); + store.add(onDidChangeData(() => { + const newData = getData(); + const diff = diffSets(oldData, newData); + for (const r of diff.removed) { + map.deleteAndDispose(r); + } + for (const a of diff.added) { + map.set(a, handleItem(a)); + } + oldData = new Set(newData); + })); + store.add(map); + return store; +} diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index e6dba4a911e..9ae7c7333a5 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -8,9 +8,9 @@ import { findLastIdx } from '../../../../base/common/arraysFind.js'; import { DeferredPromise, RunOnceScheduler } from '../../../../base/common/async.js'; import { VSBuffer, decodeBase64, encodeBase64 } from '../../../../base/common/buffer.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; +import { Emitter, Event, trackSetChanges } from '../../../../base/common/event.js'; import { stringHash } from '../../../../base/common/hash.js'; -import { Disposable, DisposableMap, IDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; import { mixin } from '../../../../base/common/objects.js'; import { autorun } from '../../../../base/common/observable.js'; import * as resources from '../../../../base/common/resources.js'; @@ -1422,7 +1422,6 @@ export class DebugModel extends Disposable implements IDebugModel { private exceptionBreakpoints!: ExceptionBreakpoint[]; private dataBreakpoints!: DataBreakpoint[]; private watchExpressions!: Expression[]; - private watchExpressionChangeListeners: DisposableMap = this._register(new DisposableMap()); private instructionBreakpoints: InstructionBreakpoint[]; constructor( @@ -1446,12 +1445,14 @@ export class DebugModel extends Disposable implements IDebugModel { this._onDidChangeWatchExpressions.fire(undefined); })); + this._register(trackSetChanges( + () => new Set(this.watchExpressions), + this.onDidChangeWatchExpressions, + (we) => we.onDidChangeValue((e) => this._onDidChangeWatchExpressionValue.fire(e))) + ); + this.instructionBreakpoints = []; this.sessions = []; - - for (const we of this.watchExpressions) { - this.watchExpressionChangeListeners.set(we.getId(), we.onDidChangeValue((e) => this._onDidChangeWatchExpressionValue.fire(e))); - } } getId(): string { @@ -2025,7 +2026,6 @@ export class DebugModel extends Disposable implements IDebugModel { addWatchExpression(name?: string): IExpression { const we = new Expression(name || ''); - this.watchExpressionChangeListeners.set(we.getId(), we.onDidChangeValue((e) => this._onDidChangeWatchExpressionValue.fire(e))); this.watchExpressions.push(we); this._onDidChangeWatchExpressions.fire(we); @@ -2043,11 +2043,6 @@ export class DebugModel extends Disposable implements IDebugModel { removeWatchExpressions(id: string | null = null): void { this.watchExpressions = id ? this.watchExpressions.filter(we => we.getId() !== id) : []; this._onDidChangeWatchExpressions.fire(undefined); - if (!id) { - this.watchExpressionChangeListeners.clearAndDisposeAll(); - return; - } - this.watchExpressionChangeListeners.deleteAndDispose(id); } moveWatchExpression(id: string, position: number): void { From ce2e3712c96c8d5287b131a48a1dbc8c06f4e85a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 10 Sep 2024 10:31:42 -0700 Subject: [PATCH 42/45] testing: implement getControllersWithTests command (#228135) Saves having to use a full test observer when only checking for presence --- .../contrib/testing/browser/testing.contribution.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index c7b89e49751..31b4073e7b4 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -235,5 +235,15 @@ CommandsRegistry.registerCommand({ } }); +CommandsRegistry.registerCommand({ + id: 'vscode.testing.getControllersWithTests', + handler: async (accessor: ServicesAccessor) => { + const testService = accessor.get(ITestService); + return [...testService.collection.rootItems] + .filter(r => r.children.size > 0) + .map(r => r.controllerId); + } +}); + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration(testingConfiguration); From 5e16ad2d31f3add6036f94864a331567098fd461 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 10 Sep 2024 19:45:09 +0200 Subject: [PATCH 43/45] Edit Context: type new line in the 'beforeinput' event of the dom node on typeInput 'insertParagraph' (#228128) emit the new line event in the beforeinput event --- .../controller/editContext/native/nativeEditContext.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts index 62749f46311..be0cc5053a8 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts @@ -78,14 +78,12 @@ export class NativeEditContext extends AbstractEditContext { if (standardKeyboardEvent.keyCode === KeyCode.KEY_IN_COMPOSITION) { standardKeyboardEvent.stopPropagation(); } - // Enter key presses are not sent as text update events, hence we need to handle them outside of the text update event - // The beforeinput and input events send `insertParagraph` and `insertLineBreak` events but only on input elements - // Hence we handle the enter key press in the keydown event - if (standardKeyboardEvent.keyCode === KeyCode.Enter) { + viewController.emitKeyDown(standardKeyboardEvent); + })); + this._register(addDisposableListener(this.domNode.domNode, 'beforeinput', async (e) => { + if (e.inputType === 'insertParagraph') { this._onType(viewController, { text: '\n', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }); } - - viewController.emitKeyDown(standardKeyboardEvent); })); // Edit context events From 38ae3ed024e8282b012027ac01e1458595fe2c19 Mon Sep 17 00:00:00 2001 From: John Murray Date: Tue, 10 Sep 2024 18:46:57 +0100 Subject: [PATCH 44/45] Correct tooltip capitalization of debug panel in status bar (fix #228088) (#228089) --- src/vs/workbench/contrib/debug/browser/debugStatus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugStatus.ts b/src/vs/workbench/contrib/debug/browser/debugStatus.ts index 9c8c5141fba..c98e63ab7b7 100644 --- a/src/vs/workbench/contrib/debug/browser/debugStatus.ts +++ b/src/vs/workbench/contrib/debug/browser/debugStatus.ts @@ -66,7 +66,7 @@ export class DebugStatusContribution implements IWorkbenchContribution { name: nls.localize('status.debug', "Debug"), text: '$(debug-alt-small) ' + text, ariaLabel: nls.localize('debugTarget', "Debug: {0}", text), - tooltip: nls.localize('selectAndStartDebug', "Select and start debug configuration"), + tooltip: nls.localize('selectAndStartDebug', "Select and Start Debug Configuration"), command: 'workbench.action.debug.selectandstart' }; } From 64dcee7906413348556e9e22393cbd6cd59e8d98 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 10 Sep 2024 20:21:54 +0200 Subject: [PATCH 45/45] Edit Context: Update the edit context on config change (#228138) add code to update the edit context on config change --- src/vs/editor/browser/view.ts | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index 13a63145c34..0ca1ceedb27 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -91,7 +91,9 @@ export class View extends ViewEventHandler { private readonly _glyphMarginWidgets: GlyphMarginWidgets; private readonly _viewCursors: ViewCursors; private readonly _viewParts: ViewPart[]; + private readonly _viewController: ViewController; + private _experimentalEditContextEnabled: boolean; private _editContext: AbstractEditContext; private readonly _pointerHandler: PointerHandler; @@ -117,7 +119,7 @@ export class View extends ViewEventHandler { this._selections = [new Selection(1, 1, 1, 1)]; this._renderAnimationFrame = null; - const viewController = new ViewController(configuration, model, userInputEvents, commandDelegate); + this._viewController = new ViewController(configuration, model, userInputEvents, commandDelegate); // The view context is passed on to most classes (basically to reduce param. counts in ctors) this._context = new ViewContext(configuration, colorTheme, model); @@ -128,10 +130,8 @@ export class View extends ViewEventHandler { this._viewParts = []; // Keyboard handler - const editContextEnabled = this._context.configuration.options.get(EditorOption.experimentalEditContextEnabled); - this._editContext = editContextEnabled - ? this._instantiationService.createInstance(NativeEditContext, this._context, viewController) - : this._instantiationService.createInstance(TextAreaEditContext, this._context, viewController, this._createTextAreaHandlerHelper()); + this._experimentalEditContextEnabled = this._context.configuration.options.get(EditorOption.experimentalEditContextEnabled); + this._editContext = this._instantiateEditContext(this._experimentalEditContextEnabled); this._viewParts.push(this._editContext); @@ -245,7 +245,29 @@ export class View extends ViewEventHandler { this._applyLayout(); // Pointer handler - this._pointerHandler = this._register(new PointerHandler(this._context, viewController, this._createPointerHandlerHelper())); + this._pointerHandler = this._register(new PointerHandler(this._context, this._viewController, this._createPointerHandlerHelper())); + } + + private _instantiateEditContext(experimentalEditContextEnabled: boolean): AbstractEditContext { + return experimentalEditContextEnabled + ? this._instantiationService.createInstance(NativeEditContext, this._context, this._viewController) + : this._instantiationService.createInstance(TextAreaEditContext, this._context, this._viewController, this._createTextAreaHandlerHelper()); + } + + private _updateEditContext(): void { + const experimentalEditContextEnabled = this._context.configuration.options.get(EditorOption.experimentalEditContextEnabled); + if (this._experimentalEditContextEnabled === experimentalEditContextEnabled) { + return; + } + this._experimentalEditContextEnabled = experimentalEditContextEnabled; + this._editContext.dispose(); + this._editContext = this._instantiateEditContext(experimentalEditContextEnabled); + this._editContext.appendTo(this._overflowGuardContainer); + // Replace the view parts with the new edit context + const indexOfEditContextHandler = this._viewParts.indexOf(this._editContext); + if (indexOfEditContextHandler !== -1) { + this._viewParts.splice(indexOfEditContextHandler, 1, this._editContext); + } } private _computeGlyphMarginLanes(): IGlyphMarginLanesModel { @@ -361,6 +383,7 @@ export class View extends ViewEventHandler { } public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { this.domNode.setClassName(this._getEditorClassName()); + this._updateEditContext(); this._applyLayout(); return false; }