From c91009aea5684f25fa7a97fda494ab82de63f3ac Mon Sep 17 00:00:00 2001 From: Cody Beyer Date: Fri, 28 Jun 2024 13:22:11 -0700 Subject: [PATCH 001/798] adding js/py lib for tagging --- .../tags/electron-sandbox/workspaceTagsService.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index 1fd7dd05886..9f731c0d526 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -229,8 +229,8 @@ const ModulesToLookFor = [ '@azure/cognitiveservices-customvision-training', '@azure/cognitiveservices-face', '@azure/cognitiveservices-translatortext', - 'microsoft-cognitiveservices-speech-sdk' - + 'microsoft-cognitiveservices-speech-sdk', + '@google/generative-ai' ]; const PyMetaModulesToLookFor = [ @@ -394,7 +394,8 @@ const PyModulesToLookFor = [ 'azure-cognitiveservices-vision-contentmoderator', 'azure-cognitiveservices-vision-face', 'azure-mgmt-cognitiveservices', - 'azure-mgmt-search' + 'azure-mgmt-search', + 'google-generativeai' ]; const GoModulesToLookFor = [ @@ -691,6 +692,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.npm.@azure/arm-kubernetesconfiguration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.react-native-macos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.react-native-windows" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@google/generative-ai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.cordova.high" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -911,6 +913,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.trulens" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.trulens-eval" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.wandb" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.google-generativeai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/storage/azfile" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, From 19ff55e467b2d2f760550b84f658ed39656bd79b Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Mon, 8 Jul 2024 11:27:19 +0200 Subject: [PATCH 002/798] zoomable hover: fix incorrect naming of object properties that resulted in that this code would set `can[Increase/Decrease]Verbosity` to undefined and break the feature --- src/vs/workbench/api/common/extHostTypes.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 92b968e001e..539f398c8d9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1205,18 +1205,18 @@ export class Hover { @es5ClassCompat export class VerboseHover extends Hover { - public canIncreaseHover: boolean | undefined; - public canDecreaseHover: boolean | undefined; + public canIncreaseVerbosity: boolean | undefined; + public canDecreaseVerbosity: boolean | undefined; constructor( contents: vscode.MarkdownString | vscode.MarkedString | (vscode.MarkdownString | vscode.MarkedString)[], range?: Range, - canIncreaseHover?: boolean, - canDecreaseHover?: boolean, + canIncreaseVerbosity?: boolean, + canDecreaseVerbosity?: boolean, ) { super(contents, range); - this.canIncreaseHover = canIncreaseHover; - this.canDecreaseHover = canDecreaseHover; + this.canIncreaseVerbosity = canIncreaseVerbosity; + this.canDecreaseVerbosity = canDecreaseVerbosity; } } From 1cd7cf5decbe06cb42efdb03c44d19e4f9b2ba58 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 8 Jul 2024 11:33:21 -0700 Subject: [PATCH 003/798] Update MD grammar (#221204) --- extensions/markdown-basics/cgmanifest.json | 2 +- .../syntaxes/markdown.tmLanguage.json | 32 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 60c6b192bed..380b0c74ac6 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -33,7 +33,7 @@ "git": { "name": "microsoft/vscode-markdown-tm-grammar", "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", - "commitHash": "f75d5f55730e72ee7ff386841949048b2395e440" + "commitHash": "7418dd20d76c72e82fadee2909e03239e9973b35" } }, "license": "MIT", diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index c84c468b80c..9761ca716ab 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/f75d5f55730e72ee7ff386841949048b2395e440", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/7418dd20d76c72e82fadee2909e03239e9973b35", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2480,14 +2480,34 @@ "name": "meta.separator.markdown" }, "frontMatter": { - "begin": "\\A-{3}\\s*$", - "contentName": "meta.embedded.block.frontmatter", + "begin": "\\A(?=(-{3,}))", + "end": "^ {,3}\\1-*[ \\t]*$|^[ \\t]*\\.{3}$", + "applyEndPatternLast": 1, + "endCaptures": { + "0": { + "name": "punctuation.definition.end.frontmatter" + } + }, "patterns": [ { - "include": "source.yaml" + "begin": "\\A(-{3,})(.*)$", + "while": "^(?! {,3}\\1-*[ \\t]*$|[ \\t]*\\.{3}$)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.begin.frontmatter" + }, + "2": { + "name": "comment.frontmatter" + } + }, + "contentName": "meta.embedded.block.frontmatter", + "patterns": [ + { + "include": "source.yaml" + } + ] } - ], - "end": "(^|\\G)-{3}|\\.{3}\\s*$" + ] }, "table": { "name": "markup.table.markdown", From f3930566bd216a35403d020ed34460f36721a951 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:51:52 +0200 Subject: [PATCH 004/798] =?UTF-8?q?Git=20-=20=F0=9F=92=84=20fix=20log=20me?= =?UTF-8?q?ssage=20format=20(#221218)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index d4e00449e20..48e66d5e9ff 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -325,7 +325,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } */ this.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length }); - this.logger.info(`[Model][doInitialScan] Initial repository scan completed - repositories(${this.repositories.length}), closed repositories (${this.closedRepositories.length}), parent repositories (${this.parentRepositories.length}), unsafe repositories (${this.unsafeRepositories.length})`); + this.logger.info(`[Model][doInitialScan] Initial repository scan completed - repositories (${this.repositories.length}), closed repositories (${this.closedRepositories.length}), parent repositories (${this.parentRepositories.length}), unsafe repositories (${this.unsafeRepositories.length})`); } /** From 63274b60e78b1ad48e9e945c59850c6e3c5d7be8 Mon Sep 17 00:00:00 2001 From: Teik Seong <53546313+mxts@users.noreply.github.com> Date: Tue, 9 Jul 2024 03:32:44 +0800 Subject: [PATCH 005/798] add option to dock terminal at top (#207721) * add option to dock terminal at top * fixed formatting * Update editorPart.ts requested changes * panel position fixes and cleanup --------- Co-authored-by: BeniBenj --- src/vs/workbench/browser/layout.ts | 75 +++++++++++-------- .../browser/parts/editor/editorPart.ts | 4 + .../browser/parts/panel/media/panelpart.css | 17 +++++ .../browser/parts/panel/panelActions.ts | 12 +-- .../browser/parts/panel/panelPart.ts | 15 +++- .../browser/parts/views/viewPaneContainer.ts | 7 +- .../browser/workbench.contribution.ts | 4 +- .../contrib/terminal/browser/terminalGroup.ts | 18 +++-- .../terminal/browser/terminalInstance.ts | 4 +- .../services/layout/browser/layoutService.ts | 11 ++- 10 files changed, 108 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index b8232cb8490..6ad52a2695e 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -12,7 +12,7 @@ import { isWindows, isLinux, isMacintosh, isWeb, isIOS } from 'vs/base/common/pl import { EditorInputCapabilities, GroupIdentifier, isResourceEditorInput, IUntypedEditorInput, pathsToEditors } from 'vs/workbench/common/editor'; import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; -import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment, ActivityBarPosition, LayoutSettings, MULTI_WINDOW_PARTS, SINGLE_WINDOW_PARTS, ZenModeSettings, EditorTabsMode, EditorActionsLocation, shouldShowCustomTitleBar } from 'vs/workbench/services/layout/browser/layoutService'; +import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment, ActivityBarPosition, LayoutSettings, MULTI_WINDOW_PARTS, SINGLE_WINDOW_PARTS, ZenModeSettings, EditorTabsMode, EditorActionsLocation, shouldShowCustomTitleBar, isHorizontal } from 'vs/workbench/services/layout/browser/layoutService'; import { isTemporaryWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -1266,18 +1266,17 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const containerDimension = this.getContainerDimension(container); if (container === this.mainContainer) { - const panelPosition = this.getPanelPosition(); - const isColumn = panelPosition === Position.RIGHT || panelPosition === Position.LEFT; + const isPanelHorizontal = isHorizontal(this.getPanelPosition()); const takenWidth = (this.isVisible(Parts.ACTIVITYBAR_PART) ? this.activityBarPartView.minimumWidth : 0) + (this.isVisible(Parts.SIDEBAR_PART) ? this.sideBarPartView.minimumWidth : 0) + - (this.isVisible(Parts.PANEL_PART) && isColumn ? this.panelPartView.minimumWidth : 0) + + (this.isVisible(Parts.PANEL_PART) && !isPanelHorizontal ? this.panelPartView.minimumWidth : 0) + (this.isVisible(Parts.AUXILIARYBAR_PART) ? this.auxiliaryBarPartView.minimumWidth : 0); const takenHeight = (this.isVisible(Parts.TITLEBAR_PART, targetWindow) ? this.titleBarPartView.minimumHeight : 0) + (this.isVisible(Parts.STATUSBAR_PART, targetWindow) ? this.statusBarPartView.minimumHeight : 0) + - (this.isVisible(Parts.PANEL_PART) && !isColumn ? this.panelPartView.minimumHeight : 0); + (this.isVisible(Parts.PANEL_PART) && isPanelHorizontal ? this.panelPartView.minimumHeight : 0); const availableWidth = containerDimension.width - takenWidth; const availableHeight = containerDimension.height - takenHeight; @@ -1546,7 +1545,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Panel Size const panelSize = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_HIDDEN) ? this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) - : (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) === Position.BOTTOM ? this.workbenchGrid.getViewSize(this.panelPartView).height : this.workbenchGrid.getViewSize(this.panelPartView).width); + : isHorizontal(this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION)) + ? this.workbenchGrid.getViewSize(this.panelPartView).height + : this.workbenchGrid.getViewSize(this.panelPartView).width; this.stateModel.setInitializationValue(LayoutStateKeys.PANEL_SIZE, panelSize as number); // Auxiliary Bar Size @@ -1635,8 +1636,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid.resizeView(this.panelPartView, { - width: viewSize.width + (this.getPanelPosition() !== Position.BOTTOM ? sizeChangePxWidth : 0), - height: viewSize.height + (this.getPanelPosition() !== Position.BOTTOM ? 0 : sizeChangePxHeight) + width: viewSize.width + (isHorizontal(this.getPanelPosition()) ? 0 : sizeChangePxWidth), + height: viewSize.height + (isHorizontal(this.getPanelPosition()) ? sizeChangePxHeight : 0) }); break; @@ -1770,8 +1771,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private adjustPartPositions(sideBarPosition: Position, panelAlignment: PanelAlignment, panelPosition: Position): void { // Move activity bar and side bars - const sideBarSiblingToEditor = panelPosition !== Position.BOTTOM || !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); - const auxiliaryBarSiblingToEditor = panelPosition !== Position.BOTTOM || !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); + const isPanelVertical = !isHorizontal(panelPosition); + const sideBarSiblingToEditor = isPanelVertical || !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); + const auxiliaryBarSiblingToEditor = isPanelVertical || !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); const preMovePanelWidth = !this.isVisible(Parts.PANEL_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) ?? this.panelPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.panelPartView).width; const preMovePanelHeight = !this.isVisible(Parts.PANEL_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) ?? this.panelPartView.minimumHeight) : this.workbenchGrid.getViewSize(this.panelPartView).height; const preMoveSideBarSize = !this.isVisible(Parts.SIDEBAR_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.sideBarPartView) ?? this.sideBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.sideBarPartView).width; @@ -1797,7 +1799,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // We moved all the side parts based on the editor and ignored the panel // Now, we need to put the panel back in the right position when it is next to the editor - if (panelPosition !== Position.BOTTOM) { + if (isPanelVertical) { this.workbenchGrid.moveView(this.panelPartView, preMovePanelWidth, this.editorPartView, panelPosition === Position.LEFT ? Direction.Left : Direction.Right); this.workbenchGrid.resizeView(this.panelPartView, { height: preMovePanelHeight as number, @@ -1824,8 +1826,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi setPanelAlignment(alignment: PanelAlignment, skipLayout?: boolean): void { - // Panel alignment only applies to a panel in the bottom position - if (this.getPanelPosition() !== Position.BOTTOM) { + // Panel alignment only applies to a panel in the top/bottom position + if (!isHorizontal(this.getPanelPosition())) { this.setPanelPosition(Position.BOTTOM); } @@ -1921,7 +1923,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const isMaximized = this.isPanelMaximized(); if (!isMaximized) { if (this.isVisible(Parts.PANEL_PART)) { - if (panelPosition === Position.BOTTOM) { + if (isHorizontal(panelPosition)) { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT, size.height); } else { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH, size.width); @@ -1932,8 +1934,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } else { this.setEditorHidden(false); this.workbenchGrid.resizeView(this.panelPartView, { - width: panelPosition === Position.BOTTOM ? size.width : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH), - height: panelPosition === Position.BOTTOM ? this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT) : size.height + width: isHorizontal(panelPosition) ? size.width : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH), + height: isHorizontal(panelPosition) ? this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT) : size.height }); } @@ -1943,7 +1945,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private panelOpensMaximized(): boolean { // The workbench grid currently prevents us from supporting panel maximization with non-center panel alignment - if (this.getPanelAlignment() !== 'center' && this.getPanelPosition() === Position.BOTTOM) { + if (this.getPanelAlignment() !== 'center' && isHorizontal(this.getPanelPosition())) { return false; } @@ -2021,7 +2023,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi isPanelMaximized(): boolean { // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment - return (this.getPanelAlignment() === 'center' || this.getPanelPosition() !== Position.BOTTOM) && !this.isVisible(Parts.EDITOR_PART, mainWindow); + return (this.getPanelAlignment() === 'center' || !isHorizontal(this.getPanelPosition())) && !this.isVisible(Parts.EDITOR_PART, mainWindow); } getSideBarPosition(): Position { @@ -2097,14 +2099,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Save the current size of the panel for the new orthogonal direction // If moving down, save the width of the panel // Otherwise, save the height of the panel - if (position === Position.BOTTOM) { + if (isHorizontal(position)) { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH, size.width); - } else if (positionFromString(oldPositionValue) === Position.BOTTOM) { + } else if (isHorizontal(positionFromString(oldPositionValue))) { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT, size.height); } } - if (position === Position.BOTTOM && this.getPanelAlignment() !== 'center' && editorHidden) { + if (isHorizontal(position) && this.getPanelAlignment() !== 'center' && editorHidden) { this.toggleMaximizedPanel(); editorHidden = false; } @@ -2116,6 +2118,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (position === Position.BOTTOM) { this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.height : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT), this.editorPartView, Direction.Down); + } else if (position === Position.TOP) { + this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.height : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT), this.editorPartView, Direction.Up); } else if (position === Position.RIGHT) { this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.width : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH), this.editorPartView, Direction.Right); } else { @@ -2133,7 +2137,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.setAuxiliaryBarHidden(true); } - if (position === Position.BOTTOM) { + if (isHorizontal(position)) { this.adjustPartPositions(this.getSideBarPosition(), this.getPanelAlignment(), position); } @@ -2242,17 +2246,20 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const auxiliaryBarSize = this.stateModel.getRuntimeValue(LayoutStateKeys.AUXILIARYBAR_HIDDEN) ? 0 : nodes.auxiliaryBar.size; const panelSize = this.stateModel.getInitializationValue(LayoutStateKeys.PANEL_SIZE) ? 0 : nodes.panel.size; + const panelPostion = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION); + const sideBarPosition = this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_POSITON); + const result = [] as ISerializedNode[]; - if (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) !== Position.BOTTOM) { + if (!isHorizontal(panelPostion)) { result.push(nodes.editor); nodes.editor.size = availableWidth - activityBarSize - sideBarSize - panelSize - auxiliaryBarSize; - if (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) === Position.RIGHT) { + if (panelPostion === Position.RIGHT) { result.push(nodes.panel); } else { result.splice(0, 0, nodes.panel); } - if (this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_POSITON) === Position.LEFT) { + if (sideBarPosition === Position.LEFT) { result.push(nodes.auxiliaryBar); result.splice(0, 0, nodes.sideBar); result.splice(0, 0, nodes.activityBar); @@ -2263,18 +2270,20 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } else { const panelAlignment = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_ALIGNMENT); - const sideBarPosition = this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_POSITON); const sideBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); const auxiliaryBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); const editorSectionWidth = availableWidth - activityBarSize - (sideBarNextToEditor ? 0 : sideBarSize) - (auxiliaryBarNextToEditor ? 0 : auxiliaryBarSize); + + const editorNodes = this.arrangeEditorNodes({ + editor: nodes.editor, + sideBar: sideBarNextToEditor ? nodes.sideBar : undefined, + auxiliaryBar: auxiliaryBarNextToEditor ? nodes.auxiliaryBar : undefined + }, availableHeight - panelSize, editorSectionWidth); + result.push({ type: 'branch', - data: [this.arrangeEditorNodes({ - editor: nodes.editor, - sideBar: sideBarNextToEditor ? nodes.sideBar : undefined, - auxiliaryBar: auxiliaryBarNextToEditor ? nodes.auxiliaryBar : undefined - }, availableHeight - panelSize, editorSectionWidth), nodes.panel], + data: panelPostion === Position.BOTTOM ? [editorNodes, nodes.panel] : [nodes.panel, editorNodes], size: editorSectionWidth }); @@ -2417,7 +2426,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi panelVisible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether or the not the panel is visible' }; statusbarVisible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether or the not the status bar is visible' }; sideBarPosition: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the primary side bar is on the left or right' }; - panelPosition: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the panel is on the bottom, left, or right' }; + panelPosition: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the panel is on the top, bottom, left, or right' }; }; const layoutDescriptor: StartupLayoutEvent = { @@ -2625,7 +2634,7 @@ class LayoutStateModel extends Disposable { LayoutStateKeys.GRID_SIZE.defaultValue = { height: workbenchDimensions.height, width: workbenchDimensions.width }; LayoutStateKeys.SIDEBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); LayoutStateKeys.AUXILIARYBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); - LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? LayoutStateKeys.PANEL_POSITION.defaultValue) === Position.BOTTOM ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4; + LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? isHorizontal(LayoutStateKeys.PANEL_POSITION.defaultValue)) ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4; LayoutStateKeys.SIDEBAR_HIDDEN.defaultValue = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; // Apply all defaults diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index d785a513798..fbdfd7daf7d 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -1103,6 +1103,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { openVerticalPosition = Position.BOTTOM; } + if (e.eventData.clientY < boundingRect.top + proximity) { + openVerticalPosition = Position.TOP; + } + if (horizontalOpenerTimeout && openHorizontalPosition !== lastOpenHorizontalPosition) { clearTimeout(horizontalOpenerTimeout); horizontalOpenerTimeout = undefined; diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 40a5ee28faf..e1c147d8e88 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -17,6 +17,15 @@ border-top-width: 0; /* no border when main editor area is hiden */ } +.monaco-workbench .part.panel.top { + border-bottom-width: 1px; + border-bottom-style: solid; +} + +.monaco-workbench.nomaineditorarea .part.panel.top { + border-bottom-width: 0; /* no border when main editor area is hiden */ +} + .monaco-workbench .part.panel.right { border-left-width: 1px; border-left-style: solid; @@ -81,3 +90,11 @@ display: inline-block; transform: rotate(90deg); } + +/* Rotate icons when panel is on left */ +.monaco-workbench .part.basepanel.top .title-actions .codicon-split-horizontal::before, +.monaco-workbench .part.basepanel.top .global-actions .codicon-panel-maximize::before, +.monaco-workbench .part.basepanel.top .global-actions .codicon-panel-restore::before { + display: inline-block; + transform: rotate(180deg); +} diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 534db0283a7..3e97968289d 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -8,7 +8,7 @@ import { localize, localize2 } from 'vs/nls'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { MenuId, MenuRegistry, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, PanelAlignment, Parts, Position, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; +import { ActivityBarPosition, isHorizontal, IWorkbenchLayoutService, LayoutSettings, PanelAlignment, Parts, Position, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelPositionContext, PanelVisibleContext } from 'vs/workbench/common/contextkeys'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { Codicon } from 'vs/base/common/codicons'; @@ -99,6 +99,7 @@ const PositionPanelActionId = { LEFT: 'workbench.action.positionPanelLeft', RIGHT: 'workbench.action.positionPanelRight', BOTTOM: 'workbench.action.positionPanelBottom', + TOP: 'workbench.action.positionPanelTop' }; const AlignPanelActionId = { @@ -136,6 +137,7 @@ function createAlignmentPanelActionConfig(id: string, title: ICommandActionTitle const PositionPanelActionConfigs: PanelActionConfig[] = [ + createPositionPanelActionConfig(PositionPanelActionId.TOP, localize2('positionPanelTop', "Move Panel To Top"), localize('positionPanelTopShort', "Top"), Position.TOP), createPositionPanelActionConfig(PositionPanelActionId.LEFT, localize2('positionPanelLeft', "Move Panel Left"), localize('positionPanelLeftShort', "Left"), Position.LEFT), createPositionPanelActionConfig(PositionPanelActionId.RIGHT, localize2('positionPanelRight', "Move Panel Right"), localize('positionPanelRightShort', "Right"), Position.RIGHT), createPositionPanelActionConfig(PositionPanelActionId.BOTTOM, localize2('positionPanelBottom', "Move Panel To Bottom"), localize('positionPanelBottomShort', "Bottom"), Position.BOTTOM), @@ -158,7 +160,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { order: 4 }); -PositionPanelActionConfigs.forEach(positionPanelAction => { +PositionPanelActionConfigs.forEach((positionPanelAction, index) => { const { id, title, shortLabel, value, when } = positionPanelAction; registerAction2(class extends Action2 { @@ -182,7 +184,7 @@ PositionPanelActionConfigs.forEach(positionPanelAction => { title: shortLabel, toggled: when.negate() }, - order: 5 + order: 5 + index }); }); @@ -280,7 +282,7 @@ registerAction2(class extends Action2 { tooltip: localize('maximizePanel', "Maximize Panel Size"), category: Categories.View, f1: true, - icon: maximizeIcon, + icon: maximizeIcon, // This is being rotated in CSS depending on the panel position // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment precondition: ContextKeyExpr.or(PanelAlignmentContext.isEqualTo('center'), PanelPositionContext.notEqualsTo('bottom')), toggled: { condition: PanelMaximizedContext, icon: restoreIcon, tooltip: localize('minimizePanel', "Restore Panel Size") }, @@ -296,7 +298,7 @@ registerAction2(class extends Action2 { run(accessor: ServicesAccessor) { const layoutService = accessor.get(IWorkbenchLayoutService); const notificationService = accessor.get(INotificationService); - if (layoutService.getPanelAlignment() !== 'center' && layoutService.getPanelPosition() === Position.BOTTOM) { + if (layoutService.getPanelAlignment() !== 'center' && isHorizontal(layoutService.getPanelPosition())) { notificationService.warn(localize('panelMaxNotSupported', "Maximizing the panel is only supported when it is center aligned.")); return; } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 845274fa940..899d97b4cda 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -112,6 +112,7 @@ export class PanelPart extends AbstractPaneCompositePart { const borderColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder) || ''; container.style.borderLeftColor = borderColor; container.style.borderRightColor = borderColor; + container.style.borderBottomColor = borderColor; const title = this.getTitleArea(); if (title) { @@ -166,10 +167,16 @@ export class PanelPart extends AbstractPaneCompositePart { override layout(width: number, height: number, top: number, left: number): void { let dimensions: Dimension; - if (this.layoutService.getPanelPosition() === Position.RIGHT) { - dimensions = new Dimension(width - 1, height); // Take into account the 1px border when layouting - } else { - dimensions = new Dimension(width, height); + switch (this.layoutService.getPanelPosition()) { + case Position.RIGHT: + dimensions = new Dimension(width - 1, height); // Take into account the 1px border when layouting + break; + case Position.TOP: + dimensions = new Dimension(width, height - 1); // Take into account the 1px border when layouting + break; + default: + dimensions = new Dimension(width, height); + break; } // Layout contents diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index d782b08c3c2..01df5c12a69 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -39,7 +39,7 @@ import { IAddedViewDescriptorRef, ICustomViewDescriptor, IView, IViewContainerMo import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchLayoutService, LayoutSettings, Position } from 'vs/workbench/services/layout/browser/layoutService'; +import { isHorizontal, IWorkbenchLayoutService, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; export const ViewsSubMenu = new MenuId('Views'); @@ -625,8 +625,9 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { case ViewContainerLocation.Sidebar: case ViewContainerLocation.AuxiliaryBar: return Orientation.VERTICAL; - case ViewContainerLocation.Panel: - return this.layoutService.getPanelPosition() === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + case ViewContainerLocation.Panel: { + return isHorizontal(this.layoutService.getPanelPosition()) ? Orientation.HORIZONTAL : Orientation.VERTICAL; + } } return Orientation.VERTICAL; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index fb05198e50b..d83ac066c6e 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -516,9 +516,9 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'workbench.panel.defaultLocation': { 'type': 'string', - 'enum': ['left', 'bottom', 'right'], + 'enum': ['left', 'bottom', 'top', 'right'], 'default': 'bottom', - 'description': localize('panelDefaultLocation', "Controls the default location of the panel (Terminal, Debug Console, Output, Problems) in a new workspace. It can either show at the bottom, right, or left of the editor area."), + 'description': localize('panelDefaultLocation', "Controls the default location of the panel (Terminal, Debug Console, Output, Problems) in a new workspace. It can either show at the bottom, top, right, or left of the editor area."), }, 'workbench.panel.opensMaximized': { 'type': 'string', diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 57848596b73..8e9ddcb5b6c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -7,7 +7,7 @@ import { TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal' import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; -import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; +import { isHorizontal, IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITerminalInstance, Direction, ITerminalGroup, ITerminalInstanceService, ITerminalConfigurationService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; @@ -285,7 +285,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { if (this._container) { this.attachToElement(this._container); } - this._onPanelOrientationChanged.fire(this._terminalLocation === ViewContainerLocation.Panel && this._panelPosition === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL); + this._onPanelOrientationChanged.fire(this._terminalLocation === ViewContainerLocation.Panel && isHorizontal(this._panelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL); this._register(toDisposable(() => { if (this._container && this._groupElement) { this._groupElement.remove(); @@ -466,7 +466,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { if (!this._splitPaneContainer) { this._panelPosition = this._layoutService.getPanelPosition(); this._terminalLocation = this._viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID)!; - const orientation = this._terminalLocation === ViewContainerLocation.Panel && this._panelPosition === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + const orientation = this._terminalLocation === ViewContainerLocation.Panel && isHorizontal(this._panelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL; this._splitPaneContainer = this._instantiationService.createInstance(SplitPaneContainer, this._groupElement, orientation); this.terminalInstances.forEach(instance => this._splitPaneContainer!.split(instance, this._activeInstanceIndex + 1)); } @@ -527,7 +527,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { const newTerminalLocation = this._viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID)!; const terminalPositionChanged = newPanelPosition !== this._panelPosition || newTerminalLocation !== this._terminalLocation; if (terminalPositionChanged) { - const newOrientation = newTerminalLocation === ViewContainerLocation.Panel && newPanelPosition === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + const newOrientation = newTerminalLocation === ViewContainerLocation.Panel && isHorizontal(newPanelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL; this._splitPaneContainer.setOrientation(newOrientation); this._panelPosition = newPanelPosition; this._terminalLocation = newTerminalLocation; @@ -563,7 +563,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { } private _getOrientation(): Orientation { - return this._getPosition() === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + return isHorizontal(this._getPosition()) ? Orientation.HORIZONTAL : Orientation.VERTICAL; } resizePane(direction: Direction): void { @@ -588,10 +588,12 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { if (shouldResizePart) { + const position = this._getPosition(); const shouldShrink = - (this._getPosition() === Position.LEFT && direction === Direction.Left) || - (this._getPosition() === Position.RIGHT && direction === Direction.Right) || - (this._getPosition() === Position.BOTTOM && direction === Direction.Down); + (position === Position.LEFT && direction === Direction.Left) || + (position === Position.RIGHT && direction === Direction.Right) || + (position === Position.BOTTOM && direction === Direction.Down) || + (position === Position.TOP && direction === Direction.Up); if (shouldShrink) { resizeAmount *= -1; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index a48841d9802..6310485b23b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -79,7 +79,7 @@ import { getWorkspaceForTerminal, preparePathForShell } from 'vs/workbench/contr import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; +import { isHorizontal, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { importAMDNodeModule } from 'vs/amdX'; @@ -2413,7 +2413,7 @@ class TerminalInstanceDragAndDropController extends Disposable implements dom.ID private _getViewOrientation(): Orientation { const panelPosition = this._layoutService.getPanelPosition(); const terminalLocation = this._viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID); - return terminalLocation === ViewContainerLocation.Panel && panelPosition === Position.BOTTOM + return terminalLocation === ViewContainerLocation.Panel && isHorizontal(panelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL; } diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 2955a53995c..43e124fa05f 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -70,7 +70,12 @@ export const enum EditorActionsLocation { export const enum Position { LEFT, RIGHT, - BOTTOM + BOTTOM, + TOP +} + +export function isHorizontal(position: Position): boolean { + return position === Position.BOTTOM || position === Position.TOP; } export const enum PanelOpensMaximizedOptions { @@ -86,6 +91,7 @@ export function positionToString(position: Position): string { case Position.LEFT: return 'left'; case Position.RIGHT: return 'right'; case Position.BOTTOM: return 'bottom'; + case Position.TOP: return 'top'; default: return 'bottom'; } } @@ -93,7 +99,8 @@ export function positionToString(position: Position): string { const positionsByString: { [key: string]: Position } = { [positionToString(Position.LEFT)]: Position.LEFT, [positionToString(Position.RIGHT)]: Position.RIGHT, - [positionToString(Position.BOTTOM)]: Position.BOTTOM + [positionToString(Position.BOTTOM)]: Position.BOTTOM, + [positionToString(Position.TOP)]: Position.TOP }; export function positionFromString(str: string): Position { From 33abd96b99731bcfb9933ecd3d4410d2afe9d865 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:08:24 +0200 Subject: [PATCH 006/798] Git - add actions to incoming/outgoing header (#221221) --- extensions/git/package.json | 23 +++++++ .../common/extensionsApiProposals.ts | 3 + .../contrib/scm/browser/scmViewPane.ts | 63 ++++++++++++++----- .../actions/common/menusExtensionPoint.ts | 6 ++ ...ibSourceControlHistoryItemChangesMenu.d.ts | 7 +++ 5 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index f2445eb3978..ccac6222db9 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -16,6 +16,7 @@ "contribMergeEditorMenus", "contribMultiDiffEditorMenus", "contribDiffEditorGutterToolBarMenus", + "contribSourceControlHistoryItemChangesMenu", "contribSourceControlHistoryItemGroupMenu", "contribSourceControlHistoryItemMenu", "contribSourceControlInputBoxMenu", @@ -1915,6 +1916,28 @@ "group": "1_modification@3" } ], + "scm/historyItemChanges/title": [ + { + "command": "git.fetchRef", + "group": "navigation@1", + "when": "scmProvider == git && scmHistoryItemGroupHasRemote" + }, + { + "command": "git.pullRef", + "group": "navigation@2", + "when": "scmProvider == git && scmHistoryItemGroupHasRemote" + }, + { + "command": "git.pushRef", + "when": "scmProvider == git && scmHistoryItemGroupHasRemote", + "group": "navigation@3" + }, + { + "command": "git.publish", + "when": "scmProvider == git && !scmHistoryItemGroupHasRemote", + "group": "navigation@3" + } + ], "scm/incomingChanges": [ { "command": "git.fetchRef", diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index a3f677336d1..dd767bf8a07 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -118,6 +118,9 @@ const _allApiProposals = { contribShareMenu: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribShareMenu.d.ts', }, + contribSourceControlHistoryItemChangesMenu: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts', + }, contribSourceControlHistoryItemGroupMenu: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts', }, diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 67564cd4aab..77d39d94f9d 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -96,7 +96,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { stripIcons } from 'vs/base/common/iconLabels'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { editorSelectionBackground, foreground, inputBackground, inputForeground, listActiveSelectionForeground, registerColor, selectionBackground, transparent } from 'vs/platform/theme/common/colorRegistry'; -import { IMenuWorkbenchToolBarOptions, MenuWorkbenchToolBar, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { IMenuWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; import { clamp, rot } from 'vs/base/common/numbers'; @@ -1201,7 +1201,9 @@ class HistoryItemChangeRenderer implements ICompressibleTreeRenderer { @@ -1226,31 +1228,46 @@ class SeparatorRenderer implements ICompressibleTreeRenderer('scm.experimental.showHistoryGraph') !== true) { - const toolBar = new MenuWorkbenchToolBar(append(element, $('.actions')), MenuId.SCMChangesSeparator, { moreIcon: Codicon.gear }, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.commandService, this.telemetryService); - disposables.add(toolBar); - } + const options = { moreIcon: this.configurationService.getValue('scm.experimental.showHistoryGraph') === true ? Codicon.more : Codicon.gear } satisfies IMenuWorkbenchToolBarOptions; + const toolBar = new WorkbenchToolBar(append(element, $('.actions')), options, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.commandService, this.telemetryService); + templateDisposables.add(toolBar); - return { label, disposables }; + return { label, toolBar, elementDisposables: new DisposableStore(), templateDisposables }; } renderElement(element: ITreeNode, index: number, templateData: SeparatorTemplate, height: number | undefined): void { + const currentHistoryItemGroup = element.element.repository.provider.historyProvider?.currentHistoryItemGroup; + + // Label templateData.label.setLabel(element.element.label, undefined, { title: element.element.ariaLabel }); + + // Toolbar + const contextKeyService = this.contextKeyService.createOverlay([ + ['scmHistoryItemGroupHasRemote', !!currentHistoryItemGroup?.remote], + ]); + const menu = this.menuService.createMenu(MenuId.SCMChangesSeparator, contextKeyService); + templateData.elementDisposables.add(connectPrimaryMenu(menu, (primary, secondary) => { + templateData.toolBar.setActions(primary, secondary, [MenuId.SCMChangesSeparator]); + })); } renderCompressedElements(node: ITreeNode, void>, index: number, templateData: SeparatorTemplate, height: number | undefined): void { throw new Error('Should never happen since node is incompressible'); } - disposeTemplate(templateData: SeparatorTemplate): void { - templateData.disposables.dispose(); + disposeElement(node: ITreeNode, index: number, templateData: SeparatorTemplate, height: number | undefined): void { + templateData.elementDisposables.clear(); } + disposeTemplate(templateData: SeparatorTemplate): void { + templateData.elementDisposables.dispose(); + templateData.templateDisposables.dispose(); + } } class ListDelegate implements IListVirtualDelegate { @@ -1672,14 +1689,16 @@ MenuRegistry.appendMenuItem(MenuId.SCMChangesSeparator, { title: localize('incomingChanges', "Show Incoming Changes"), submenu: MenuId.SCMIncomingChangesSetting, group: '1_incoming&outgoing', - order: 1 + order: 1, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); MenuRegistry.appendMenuItem(Menus.ChangesSettings, { title: localize('incomingChanges', "Show Incoming Changes"), submenu: MenuId.SCMIncomingChangesSetting, group: '1_incoming&outgoing', - order: 1 + order: 1, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); registerAction2(class extends SCMChangesSettingAction { @@ -1723,14 +1742,16 @@ MenuRegistry.appendMenuItem(MenuId.SCMChangesSeparator, { title: localize('outgoingChanges', "Show Outgoing Changes"), submenu: MenuId.SCMOutgoingChangesSetting, group: '1_incoming&outgoing', - order: 2 + order: 2, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); MenuRegistry.appendMenuItem(Menus.ChangesSettings, { title: localize('outgoingChanges', "Show Outgoing Changes"), submenu: MenuId.SCMOutgoingChangesSetting, group: '1_incoming&outgoing', - order: 2 + order: 2, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); registerAction2(class extends SCMChangesSettingAction { @@ -1781,8 +1802,16 @@ registerAction2(class extends Action2 { f1: false, toggled: ContextKeyExpr.equals('config.scm.showChangesSummary', true), menu: [ - { id: MenuId.SCMChangesSeparator, order: 3 }, - { id: Menus.ChangesSettings, order: 3 }, + { + id: MenuId.SCMChangesSeparator, + order: 3, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) + }, + { + id: Menus.ChangesSettings, + order: 3, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) + }, ] }); } diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 9e34f460806..17d4e15c4fa 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -169,6 +169,12 @@ const apiMenus: IAPIMenu[] = [ description: localize('menus.input', "The Source Control input box menu"), proposed: 'contribSourceControlInputBoxMenu' }, + { + key: 'scm/historyItemChanges/title', + id: MenuId.SCMChangesSeparator, + description: localize('menus.historyItemChanges', "The Source Control incoming/outgoing changes title menu"), + proposed: 'contribSourceControlHistoryItemChangesMenu' + }, { key: 'scm/incomingChanges', id: MenuId.SCMIncomingChanges, diff --git a/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts b/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts new file mode 100644 index 00000000000..a9d758fa24b --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.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. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `scm/historyItemChanges/title`-menu contribution point +// https://github.com/microsoft/vscode/issues/201997 From 6c907814bfc223e842aa0d82e69a467ce4f76223 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 8 Jul 2024 14:40:09 -0700 Subject: [PATCH 007/798] feat: support button titles in chat confirmation api (#221223) --- src/vs/workbench/api/common/extHostChatAgents2.ts | 4 ++-- .../workbench/api/common/extHostTypeConverters.ts | 3 ++- src/vs/workbench/api/common/extHostTypes.ts | 5 ++++- .../chatConfirmationContentPart.ts | 14 ++++++++++---- .../workbench/contrib/chat/common/chatService.ts | 1 + .../vscode.proposed.chatParticipantAdditions.d.ts | 5 +++-- 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 8b2209b5363..e029c680e42 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -212,11 +212,11 @@ class ChatAgentResponseStream { _report(dto); return this; }, - confirmation(title, message, data) { + confirmation(title, message, data, buttons) { throwIfDone(this.confirmation); checkProposedApiEnabled(that._extension, 'chatParticipantAdditions'); - const part = new extHostTypes.ChatResponseConfirmationPart(title, message, data); + const part = new extHostTypes.ChatResponseConfirmationPart(title, message, data, buttons); const dto = typeConvert.ChatResponseConfirmationPart.from(part); _report(dto); return this; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6fad560a437..527139f87f4 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2354,7 +2354,8 @@ export namespace ChatResponseConfirmationPart { kind: 'confirmation', title: part.title, message: part.message, - data: part.data + data: part.data, + buttons: part.buttons }; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 18c2eefe79d..581f4020c8c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4377,10 +4377,13 @@ export class ChatResponseConfirmationPart { title: string; message: string; data: any; - constructor(title: string, message: string, data: any) { + buttons?: string[]; + + constructor(title: string, message: string, data: any, buttons?: string[]) { this.title = title; this.message = message; this.data = data; + this.buttons = buttons; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts index 4159f07c919..37486118762 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts @@ -28,10 +28,16 @@ export class ChatConfirmationContentPart extends Disposable implements IChatCont super(); const element = context.element; - const confirmationWidget = this._register(this.instantiationService.createInstance(ChatConfirmationWidget, confirmation.title, confirmation.message, [ - { label: localize('accept', "Accept"), data: confirmation.data }, - { label: localize('dismiss', "Dismiss"), data: confirmation.data, isSecondary: true }, - ])); + const buttons = confirmation.buttons + ? confirmation.buttons.map(button => ({ + label: button, + data: confirmation.data + })) + : [ + { label: localize('accept', "Accept"), data: confirmation.data }, + { label: localize('dismiss', "Dismiss"), data: confirmation.data, isSecondary: true }, + ]; + const confirmationWidget = this._register(this.instantiationService.createInstance(ChatConfirmationWidget, confirmation.title, confirmation.message, buttons)); confirmationWidget.setShowButtons(!confirmation.isUsed); this._register(confirmationWidget.onDidClick(async e => { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index f10564d7828..645ba778d36 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -160,6 +160,7 @@ export interface IChatConfirmation { title: string; message: string; data: any; + buttons?: string[]; isUsed?: boolean; kind: 'confirmation'; } diff --git a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts index cd2ec7ba919..ab43814c4f4 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts @@ -59,7 +59,8 @@ declare module 'vscode' { title: string; message: string; data: any; - constructor(title: string, message: string, data: any); + buttons?: string[]; + constructor(title: string, message: string, data: any, buttons?: string[]); } export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseDetectedParticipantPart | ChatResponseConfirmationPart; @@ -102,7 +103,7 @@ declare module 'vscode' { * TODO@API should this be MarkdownString? * TODO@API should actually be a more generic function that takes an array of buttons */ - confirmation(title: string, message: string, data: any): void; + confirmation(title: string, message: string, data: any, buttons?: string[]): void; /** * Push a warning to this stream. Short-hand for From 1b24381b5c1bc52636191a8db7b5e5e8445194a8 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 8 Jul 2024 15:19:40 -0700 Subject: [PATCH 008/798] Adopt `getAccounts` API in GitHub Authentication (#221224) This allows the GitHub Auth provider to take in a account per this proposal: https://github.com/microsoft/vscode/blob/417dddb83f3536479f88c40e1d78edf1136a5ae5/src/vscode-dts/vscode.proposed.authGetSessions.d.ts#L35-L69 Additionally, this makes an additional small change that allows `clearSessionPreference` to work in single-account auth providers. --- extensions/github-authentication/package.json | 3 + extensions/github-authentication/src/flows.ts | 4 ++ .../github-authentication/src/github.ts | 67 ++++++++++++++++--- .../github-authentication/tsconfig.json | 3 +- .../api/browser/mainThreadAuthentication.ts | 24 +++---- 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 2d2bea56277..ed024ee59b8 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -13,6 +13,9 @@ "Other" ], "api": "none", + "enabledApiProposals": [ + "authGetSessions" + ], "extensionKind": [ "ui", "workspace" diff --git a/extensions/github-authentication/src/flows.ts b/extensions/github-authentication/src/flows.ts index 7498a2b2202..a2497b2b0b2 100644 --- a/extensions/github-authentication/src/flows.ts +++ b/extensions/github-authentication/src/flows.ts @@ -173,6 +173,8 @@ const allFlows: IFlow[] = [ ]); if (existingLogin) { searchParams.append('login', existingLogin); + } else { + searchParams.append('prompt', 'select_account'); } // The extra toString, parse is apparently needed for env.openExternal @@ -240,6 +242,8 @@ const allFlows: IFlow[] = [ ]); if (existingLogin) { searchParams.append('login', existingLogin); + } else { + searchParams.append('prompt', 'select_account'); } const loginUrl = baseUri.with({ diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 15fe2ef04f8..08fb16730e6 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -11,7 +11,7 @@ import { PromiseAdapter, arrayEquals, promiseFromEvent } from './common/utils'; import { ExperimentationTelemetry } from './common/experimentationService'; import { Log } from './common/logger'; import { crypto } from './node/crypto'; -import { TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; +import { CANCELLATION_ERROR, TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; interface SessionData { id: string; @@ -148,14 +148,17 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid return this._sessionChangeEmitter.event; } - async getSessions(scopes?: string[]): Promise { + async getSessions(scopes: string[] | undefined, options?: vscode.AuthenticationProviderSessionOptions): Promise { // For GitHub scope list, order doesn't matter so we immediately sort the scopes const sortedScopes = scopes?.sort() || []; this._logger.info(`Getting sessions for ${sortedScopes.length ? sortedScopes.join(',') : 'all scopes'}...`); const sessions = await this._sessionsPromise; - const finalSessions = sortedScopes.length - ? sessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes)) + const accountFilteredSessions = options?.account + ? sessions.filter(session => session.account.label === options.account?.label) : sessions; + const finalSessions = sortedScopes.length + ? accountFilteredSessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes)) + : accountFilteredSessions; this._logger.info(`Got ${finalSessions.length} sessions for ${sortedScopes?.join(',') ?? 'all scopes'}...`); return finalSessions; @@ -279,7 +282,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid this._logger.info(`Stored ${sessions.length} sessions!`); } - public async createSession(scopes: string[]): Promise { + public async createSession(scopes: string[], options?: vscode.AuthenticationProviderSessionOptions): Promise { try { // For GitHub scope list, order doesn't matter so we use a sorted scope to determine // if we've got a session already. @@ -298,11 +301,59 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid const sessions = await this._sessionsPromise; - const accounts = new Set(sessions.map(session => session.account.label)); - const existingLogin = accounts.size <= 1 ? sessions[0]?.account.label : await vscode.window.showQuickPick([...accounts], { placeHolder: 'Choose an account that you would like to log in to' }); + let forcedLogin = options?.account?.label; + let backupLogin: string | undefined; + if (!forcedLogin) { + const accounts = new Set(sessions.map(session => session.account.label)); + this._logger.info(`Found ${accounts.size} accounts.`); + // This helps us tell GitHub that we're already logged in to an account/accounts + // and should probably use it. The user _can_ sign in to a different account + // if they want to, in the browser, but this is a good default for the happy path. + if (accounts.size > 1) { + // If there are multiple accounts, we should prompt the user to choose one. + const newAccount = vscode.l10n.t('New account...'); + const accountChoiceResult = await vscode.window.showQuickPick( + [...accounts, newAccount], + { placeHolder: vscode.l10n.t('Choose an account that you would like to log in to') } + ); + forcedLogin = accountChoiceResult === newAccount ? undefined : accountChoiceResult; + } else { + // If there is only one account, we can use that to seed the login, but + // we don't want to force the user to use it. + backupLogin = sessions[0]?.account.label; + } + } + this._logger.info(`Logging in with '${forcedLogin ? forcedLogin : 'any'}' account...`); + const scopeString = sortedScopes.join(' '); - const token = await this._githubServer.login(scopeString, existingLogin); + const token = await this._githubServer.login(scopeString, forcedLogin ?? backupLogin); const session = await this.tokenToSession(token, scopes); + + // If an account was specified, we should ensure that the token we got back is for that account. + if (forcedLogin) { + if (session.account.label !== forcedLogin) { + const keepNewAccount = vscode.l10n.t('Keep {0}', session.account.label); + const tryAgain = vscode.l10n.t('Login with {0}', forcedLogin); + const result = await vscode.window.showWarningMessage( + vscode.l10n.t('Incorrect account detected'), + { modal: true, detail: vscode.l10n.t('The chosen account, {0}, does not match the requested account, {1}.', session.account.label, forcedLogin) }, + keepNewAccount, + tryAgain + ); + if (result === tryAgain) { + return await this.createSession(scopes, { + ...options, + // The id doesn't matter here, we just need to pass the label through + account: { id: forcedLogin, label: forcedLogin } + }); + } + // Cancelled result + if (!result) { + throw new Error(CANCELLATION_ERROR); + } + // Keep result continues on + } + } this.afterSessionLoad(session); const sessionIndex = sessions.findIndex(s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes)); diff --git a/extensions/github-authentication/tsconfig.json b/extensions/github-authentication/tsconfig.json index 5e4713e9f3b..70eba984a26 100644 --- a/extensions/github-authentication/tsconfig.json +++ b/extensions/github-authentication/tsconfig.json @@ -12,6 +12,7 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts" + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.authGetSessions.d.ts" ] } diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 5780ec8797e..d833eab2860 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -174,21 +174,21 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu throw new Error('Invalid combination of options. Please remove one of the following: createIfNone, silent'); } + if (options.clearSessionPreference) { + // Clearing the session preference is usually paired with createIfNone, so just remove the preference and + // defer to the rest of the logic in this function to choose the session. + this.authenticationExtensionsService.removeSessionPreference(providerId, extensionId, scopes); + } + // Check if the sessions we have are valid if (!options.forceNewSession && sessions.length) { if (provider.supportsMultipleAccounts) { - if (options.clearSessionPreference) { - // Clearing the session preference is usually paired with createIfNone, so just remove the preference and - // defer to the rest of the logic in this function to choose the session. - this.authenticationExtensionsService.removeSessionPreference(providerId, extensionId, scopes); - } else { - // If we have an existing session preference, use that. If not, we'll return any valid session at the end of this function. - const existingSessionPreference = this.authenticationExtensionsService.getSessionPreference(providerId, extensionId, scopes); - if (existingSessionPreference) { - const matchingSession = sessions.find(session => session.id === existingSessionPreference); - if (matchingSession && this.authenticationAccessService.isAccessAllowed(providerId, matchingSession.account.label, extensionId)) { - return matchingSession; - } + // If we have an existing session preference, use that. If not, we'll return any valid session at the end of this function. + const existingSessionPreference = this.authenticationExtensionsService.getSessionPreference(providerId, extensionId, scopes); + if (existingSessionPreference) { + const matchingSession = sessions.find(session => session.id === existingSessionPreference); + if (matchingSession && this.authenticationAccessService.isAccessAllowed(providerId, matchingSession.account.label, extensionId)) { + return matchingSession; } } } else if (this.authenticationAccessService.isAccessAllowed(providerId, sessions[0].account.label, extensionId)) { From b2af4e7d34cc7bb96b5c0589549e1ffa15ee0e3b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 8 Jul 2024 15:27:24 -0700 Subject: [PATCH 009/798] testing: add stack trace proposal and split out test results view The test results view was 2k+ lines, time to split it up a bit. --- .../common/extensionsApiProposals.ts | 3 + .../workbench/api/common/extHost.api.impl.ts | 2 + .../api/common/extHostTypeConverters.ts | 5 + src/vs/workbench/api/common/extHostTypes.ts | 17 +- .../testResultsView/testResultsOutput.ts | 545 +++++++ .../testResultsView/testResultsSubject.ts | 94 ++ .../testResultsView/testResultsTree.ts | 853 ++++++++++ .../testing/browser/testingOutputPeek.ts | 1431 +---------------- .../contrib/testing/common/testTypes.ts | 33 + ...vscode.proposed.testMessageStackTrace.d.ts | 38 + 10 files changed, 1606 insertions(+), 1415 deletions(-) create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts create mode 100644 src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index dd767bf8a07..0429fdd1510 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -344,6 +344,9 @@ const _allApiProposals = { terminalShellIntegration: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellIntegration.d.ts', }, + testMessageStackTrace: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts', + }, testObserver: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts', }, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index b6b4f5746bb..7847141b2f1 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1686,6 +1686,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TestResultState: extHostTypes.TestResultState, TestRunRequest: extHostTypes.TestRunRequest, TestMessage: extHostTypes.TestMessage, + TestMessage2: extHostTypes.TestMessage, + TestMessageStackFrame: extHostTypes.TestMessageStackFrame, TestTag: extHostTypes.TestTag, TestRunProfileKind: extHostTypes.TestRunProfileKind, TextSearchCompleteMessageType: TextSearchCompleteMessageType, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6fad560a437..f3689b50932 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1888,6 +1888,11 @@ export namespace TestMessage { actual: message.actualOutput, contextValue: message.contextValue, location: message.location && ({ range: Range.from(message.location.range), uri: message.location.uri }), + stackTrace: (message as vscode.TestMessage2).stackTrace?.map(s => ({ + label: s.label, + position: s.position && Position.from(s.position), + uri: s.file && URI.revive(s.file).toJSON(), + })), }; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 18c2eefe79d..b757ae7e060 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4069,9 +4069,11 @@ export class TestMessage implements vscode.TestMessage { public expectedOutput?: string; public actualOutput?: string; public location?: vscode.Location; - /** proposed: */ public contextValue?: string; + /** proposed: */ + public stackTrace?: TestMessageStackFrame[]; + public static diff(message: string | vscode.MarkdownString, expected: string, actual: string) { const msg = new TestMessage(message); msg.expectedOutput = expected; @@ -4087,6 +4089,19 @@ export class TestTag implements vscode.TestTag { constructor(public readonly id: string) { } } +export class TestMessageStackFrame { + /** + * @param label The name of the stack frame + * @param file The file URI of the stack frame + * @param position The position of the stack frame within the file + */ + constructor( + public label: string, + public file?: vscode.Uri, + public position?: Position, + ) { } +} + //#endregion //#region Test Coverage diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts new file mode 100644 index 00000000000..673f895b854 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts @@ -0,0 +1,545 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/browser/dom'; +import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Delayer } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { Iterable } from 'vs/base/common/iterator'; +import { Lazy } from 'vs/base/common/lazy'; +import { Disposable, IDisposable, IReference, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; +import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; +import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { peekViewResultsBackground } from 'vs/editor/contrib/peekView/browser/peekView'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; +import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { EditorModel } from 'vs/workbench/common/editor/editorModel'; +import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { DetachedProcessInfo } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; +import { IDetachedTerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; +import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { colorizeTestMessageInEditor } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { Testing } from 'vs/workbench/contrib/testing/common/constants'; +import { MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; +import { ITaskRawOutput, ITestResult, ITestRunTaskResults, LiveTestResult, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestMessage, TestMessageType, getMarkId } from 'vs/workbench/contrib/testing/common/testTypes'; + + +class SimpleDiffEditorModel extends EditorModel { + public readonly original = this._original.object.textEditorModel; + public readonly modified = this._modified.object.textEditorModel; + + constructor( + private readonly _original: IReference, + private readonly _modified: IReference, + ) { + super(); + } + + public override dispose() { + super.dispose(); + this._original.dispose(); + this._modified.dispose(); + } +} + + +export interface IPeekOutputRenderer extends IDisposable { + /** Updates the displayed test. Should clear if it cannot display the test. */ + update(subject: InspectSubject): void; + /** Recalculate content layout. */ + layout(dimension: dom.IDimension): void; + /** Dispose the content provider. */ + dispose(): void; +} + +const commonEditorOptions: IEditorOptions = { + scrollBeyondLastLine: false, + links: true, + lineNumbers: 'off', + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false + }, + fixedOverflowWidgets: true, + readOnly: true, + minimap: { + enabled: false + }, + wordWrap: 'on', +}; + +const diffEditorOptions: IDiffEditorConstructionOptions = { + ...commonEditorOptions, + enableSplitViewResizing: true, + isInEmbeddedEditor: true, + renderOverviewRuler: false, + ignoreTrimWhitespace: false, + renderSideBySide: true, + useInlineViewWhenSpaceIsLimited: false, + originalAriaLabel: localize('testingOutputExpected', 'Expected result'), + modifiedAriaLabel: localize('testingOutputActual', 'Actual result'), + diffAlgorithm: 'advanced', +}; + + +export class DiffContentProvider extends Disposable implements IPeekOutputRenderer { + private readonly widget = this._register(new MutableDisposable()); + private readonly model = this._register(new MutableDisposable()); + private dimension?: dom.IDimension; + + constructor( + private readonly editor: ICodeEditor | undefined, + private readonly container: HTMLElement, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextModelService private readonly modelService: ITextModelService, + ) { + super(); + } + + public async update(subject: InspectSubject) { + if (!(subject instanceof MessageSubject)) { + return this.clear(); + } + const message = subject.message; + if (!ITestMessage.isDiffable(message)) { + return this.clear(); + } + + const [original, modified] = await Promise.all([ + this.modelService.createModelReference(subject.expectedUri), + this.modelService.createModelReference(subject.actualUri), + ]); + + const model = this.model.value = new SimpleDiffEditorModel(original, modified); + if (!this.widget.value) { + this.widget.value = this.editor ? this.instantiationService.createInstance( + EmbeddedDiffEditorWidget, + this.container, + diffEditorOptions, + {}, + this.editor, + ) : this.instantiationService.createInstance( + DiffEditorWidget, + this.container, + diffEditorOptions, + {}, + ); + + if (this.dimension) { + this.widget.value.layout(this.dimension); + } + } + + this.widget.value.setModel(model); + this.widget.value.updateOptions(this.getOptions( + isMultiline(message.expected) || isMultiline(message.actual) + )); + } + + private clear() { + this.model.clear(); + this.widget.clear(); + } + + public layout(dimensions: dom.IDimension) { + this.dimension = dimensions; + this.widget.value?.layout(dimensions); + } + + protected getOptions(isMultiline: boolean): IDiffEditorOptions { + return isMultiline + ? { ...diffEditorOptions, lineNumbers: 'on' } + : { ...diffEditorOptions, lineNumbers: 'off' }; + } +} + +class ScrollableMarkdownMessage extends Disposable { + private readonly scrollable: DomScrollableElement; + private readonly element: HTMLElement; + + constructor(container: HTMLElement, markdown: MarkdownRenderer, message: IMarkdownString) { + super(); + + const rendered = this._register(markdown.render(message, {})); + rendered.element.style.height = '100%'; + rendered.element.style.userSelect = 'text'; + container.appendChild(rendered.element); + this.element = rendered.element; + + this.scrollable = this._register(new DomScrollableElement(rendered.element, { + className: 'preview-text', + })); + container.appendChild(this.scrollable.getDomNode()); + + this._register(toDisposable(() => { + this.scrollable.getDomNode().remove(); + })); + + this.scrollable.scanDomNode(); + } + + public layout(height: number, width: number) { + // Remove padding of `.monaco-editor .zone-widget.test-output-peek .preview-text` + this.scrollable.setScrollDimensions({ + width: width - 32, + height: height - 16, + scrollWidth: this.element.scrollWidth, + scrollHeight: this.element.scrollHeight + }); + } +} + +export class MarkdownTestMessagePeek extends Disposable implements IPeekOutputRenderer { + private readonly markdown = new Lazy( + () => this._register(this.instantiationService.createInstance(MarkdownRenderer, {})), + ); + + private readonly textPreview = this._register(new MutableDisposable()); + + constructor(private readonly container: HTMLElement, @IInstantiationService private readonly instantiationService: IInstantiationService) { + super(); + } + + public update(subject: InspectSubject): void { + if (!(subject instanceof MessageSubject)) { + return this.textPreview.clear(); + } + + const message = subject.message; + if (ITestMessage.isDiffable(message) || typeof message.message === 'string') { + return this.textPreview.clear(); + } + + this.textPreview.value = new ScrollableMarkdownMessage( + this.container, + this.markdown.value, + message.message as IMarkdownString, + ); + } + + public layout(dimension: dom.IDimension): void { + this.textPreview.value?.layout(dimension.height, dimension.width); + } +} + +export class PlainTextMessagePeek extends Disposable implements IPeekOutputRenderer { + private readonly widgetDecorations = this._register(new MutableDisposable()); + private readonly widget = this._register(new MutableDisposable()); + private readonly model = this._register(new MutableDisposable()); + private dimension?: dom.IDimension; + + constructor( + private readonly editor: ICodeEditor | undefined, + private readonly container: HTMLElement, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextModelService private readonly modelService: ITextModelService, + ) { + super(); + } + + public async update(subject: InspectSubject) { + if (!(subject instanceof MessageSubject)) { + return this.clear(); + } + + const message = subject.message; + if (ITestMessage.isDiffable(message) || message.type === TestMessageType.Output || typeof message.message !== 'string') { + return this.clear(); + } + + const modelRef = this.model.value = await this.modelService.createModelReference(subject.messageUri); + if (!this.widget.value) { + this.widget.value = this.editor ? this.instantiationService.createInstance( + EmbeddedCodeEditorWidget, + this.container, + commonEditorOptions, + {}, + this.editor, + ) : this.instantiationService.createInstance( + CodeEditorWidget, + this.container, + commonEditorOptions, + { isSimpleWidget: true } + ); + + if (this.dimension) { + this.widget.value.layout(this.dimension); + } + } + + this.widget.value.setModel(modelRef.object.textEditorModel); + this.widget.value.updateOptions(commonEditorOptions); + this.widgetDecorations.value = colorizeTestMessageInEditor(message.message, this.widget.value); + } + + private clear() { + this.widgetDecorations.clear(); + this.widget.clear(); + this.model.clear(); + } + + public layout(dimensions: dom.IDimension) { + this.dimension = dimensions; + this.widget.value?.layout(dimensions); + } +} + +export class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { + private dimensions?: dom.IDimension; + private readonly terminalCwd = this._register(new MutableObservableValue('')); + private readonly xtermLayoutDelayer = this._register(new Delayer(50)); + + /** Active terminal instance. */ + private readonly terminal = this._register(new MutableDisposable()); + /** Listener for streaming result data */ + private readonly outputDataListener = this._register(new MutableDisposable()); + + constructor( + private readonly container: HTMLElement, + private readonly isInPeekView: boolean, + @ITerminalService private readonly terminalService: ITerminalService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService, + ) { + super(); + } + + private async makeTerminal() { + const prev = this.terminal.value; + if (prev) { + prev.xterm.clearBuffer(); + prev.xterm.clearSearchDecorations(); + // clearBuffer tries to retain the prompt line, but this doesn't exist for tests. + // So clear the screen (J) and move to home (H) to ensure previous data is cleaned up. + prev.xterm.write(`\x1b[2J\x1b[0;0H`); + return prev; + } + + const capabilities = new TerminalCapabilityStore(); + const cwd = this.terminalCwd; + capabilities.add(TerminalCapability.CwdDetection, { + type: TerminalCapability.CwdDetection, + get cwds() { return [cwd.value]; }, + onDidChangeCwd: cwd.onDidChange, + getCwd: () => cwd.value, + updateCwd: () => { }, + }); + + return this.terminal.value = await this.terminalService.createDetachedTerminal({ + rows: 10, + cols: 80, + readonly: true, + capabilities, + processInfo: new DetachedProcessInfo({ initialCwd: cwd.value }), + colorProvider: { + getBackgroundColor: theme => { + const terminalBackground = theme.getColor(TERMINAL_BACKGROUND_COLOR); + if (terminalBackground) { + return terminalBackground; + } + if (this.isInPeekView) { + return theme.getColor(peekViewResultsBackground); + } + const location = this.viewDescriptorService.getViewLocationById(Testing.ResultsViewId); + return location === ViewContainerLocation.Panel + ? theme.getColor(PANEL_BACKGROUND) + : theme.getColor(SIDE_BAR_BACKGROUND); + }, + } + }); + } + + public async update(subject: InspectSubject) { + this.outputDataListener.clear(); + if (subject instanceof TaskSubject) { + await this.updateForTaskSubject(subject); + } else if (subject instanceof TestOutputSubject || (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output)) { + await this.updateForTestSubject(subject); + } else { + this.clear(); + } + } + + private async updateForTestSubject(subject: TestOutputSubject | MessageSubject) { + const that = this; + const testItem = subject instanceof TestOutputSubject ? subject.test.item : subject.test; + const terminal = await this.updateGenerically({ + subject, + noOutputMessage: localize('caseNoOutput', 'The test case did not report any output.'), + getTarget: result => result?.tasks[subject.taskIndex].output, + *doInitialWrite(output, results) { + that.updateCwd(testItem.uri); + const state = subject instanceof TestOutputSubject ? subject.test : results.getStateById(testItem.extId); + if (!state) { + return; + } + + for (const message of state.tasks[subject.taskIndex].messages) { + if (message.type === TestMessageType.Output) { + yield* output.getRangeIter(message.offset, message.length); + } + } + }, + doListenForMoreData: (output, result, write) => result.onChange(e => { + if (e.reason === TestResultItemChangeReason.NewMessage && e.item.item.extId === testItem.extId && e.message.type === TestMessageType.Output) { + for (const chunk of output.getRangeIter(e.message.offset, e.message.length)) { + write(chunk.buffer); + } + } + }), + }); + + if (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output && subject.message.marker !== undefined) { + terminal?.xterm.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true); + } + } + + private updateForTaskSubject(subject: TaskSubject) { + return this.updateGenerically({ + subject, + noOutputMessage: localize('runNoOutput', 'The test run did not record any output.'), + getTarget: result => result?.tasks[subject.taskIndex], + doInitialWrite: (task, result) => { + // Update the cwd and use the first test to try to hint at the correct cwd, + // but often this will fall back to the first workspace folder. + this.updateCwd(Iterable.find(result.tests, t => !!t.item.uri)?.item.uri); + return task.output.buffers; + }, + doListenForMoreData: (task, _result, write) => task.output.onDidWriteData(e => write(e.buffer)), + }); + } + + private async updateGenerically(opts: { + subject: InspectSubject; + noOutputMessage: string; + getTarget: (result: ITestResult) => T | undefined; + doInitialWrite: (target: T, result: LiveTestResult) => Iterable; + doListenForMoreData: (target: T, result: LiveTestResult, write: (s: Uint8Array) => void) => IDisposable; + }) { + const result = opts.subject.result; + const target = opts.getTarget(result); + if (!target) { + return this.clear(); + } + + const terminal = await this.makeTerminal(); + let didWriteData = false; + + const pendingWrites = new MutableObservableValue(0); + if (result instanceof LiveTestResult) { + for (const chunk of opts.doInitialWrite(target, result)) { + didWriteData ||= chunk.byteLength > 0; + pendingWrites.value++; + terminal.xterm.write(chunk.buffer, () => pendingWrites.value--); + } + } else { + didWriteData = true; + this.writeNotice(terminal, localize('runNoOutputForPast', 'Test output is only available for new test runs.')); + } + + this.attachTerminalToDom(terminal); + this.outputDataListener.clear(); + + if (result instanceof LiveTestResult && !result.completedAt) { + const l1 = result.onComplete(() => { + if (!didWriteData) { + this.writeNotice(terminal, opts.noOutputMessage); + } + }); + const l2 = opts.doListenForMoreData(target, result, data => { + terminal.xterm.write(data); + didWriteData ||= data.byteLength > 0; + }); + + this.outputDataListener.value = combinedDisposable(l1, l2); + } + + if (!this.outputDataListener.value && !didWriteData) { + this.writeNotice(terminal, opts.noOutputMessage); + } + + // Ensure pending writes finish, otherwise the selection in `updateForTestSubject` + // can happen before the markers are processed. + if (pendingWrites.value > 0) { + await new Promise(resolve => { + const l = pendingWrites.onDidChange(() => { + if (pendingWrites.value === 0) { + l.dispose(); + resolve(); + } + }); + }); + } + + return terminal; + } + + private updateCwd(testUri?: URI) { + const wf = (testUri && this.workspaceContext.getWorkspaceFolder(testUri)) + || this.workspaceContext.getWorkspace().folders[0]; + if (wf) { + this.terminalCwd.value = wf.uri.fsPath; + } + } + + private writeNotice(terminal: IDetachedTerminalInstance, str: string) { + terminal.xterm.write(formatMessageForTerminal(str)); + } + + private attachTerminalToDom(terminal: IDetachedTerminalInstance) { + terminal.xterm.write('\x1b[?25l'); // hide cursor + dom.scheduleAtNextAnimationFrame(dom.getWindow(this.container), () => this.layoutTerminal(terminal)); + terminal.attachToElement(this.container, { enableGpu: false }); + } + + private clear() { + this.outputDataListener.clear(); + this.xtermLayoutDelayer.cancel(); + this.terminal.clear(); + } + + public layout(dimensions: dom.IDimension) { + this.dimensions = dimensions; + if (this.terminal.value) { + this.layoutTerminal(this.terminal.value, dimensions.width, dimensions.height); + } + } + + private layoutTerminal( + { xterm }: IDetachedTerminalInstance, + width = this.dimensions?.width ?? this.container.clientWidth, + height = this.dimensions?.height ?? this.container.clientHeight + ) { + width -= 10 + 20; // scrollbar width + margin + this.xtermLayoutDelayer.trigger(() => { + const scaled = getXtermScaledDimensions(dom.getWindow(this.container), xterm.getFont(), width, height); + if (scaled) { + xterm.resize(scaled.cols, scaled.rows); + } + }); + } +} + +const isMultiline = (str: string | undefined) => !!str && str.includes('\n'); diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts new file mode 100644 index 00000000000..6296138f985 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { MarshalledId } from 'vs/base/common/marshallingIds'; +import { URI } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; +import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult'; +import { IRichLocation, ITestItem, ITestMessage, ITestMessageMenuArgs, ITestRunTask, ITestTaskState, InternalTestItem, TestMessageType, TestResultItem } from 'vs/workbench/contrib/testing/common/testTypes'; +import { TestUriType, buildTestUri } from 'vs/workbench/contrib/testing/common/testingUri'; + +export const getMessageArgs = (test: TestResultItem, message: ITestMessage): ITestMessageMenuArgs => ({ + $mid: MarshalledId.TestMessageMenuArgs, + test: InternalTestItem.serialize(test), + message: ITestMessage.serialize(message), +}); + +export class MessageSubject { + public readonly test: ITestItem; + public readonly message: ITestMessage; + public readonly expectedUri: URI; + public readonly actualUri: URI; + public readonly messageUri: URI; + public readonly revealLocation: IRichLocation | undefined; + public readonly context: ITestMessageMenuArgs | undefined; + + public get isDiffable() { + return this.message.type === TestMessageType.Error && ITestMessage.isDiffable(this.message); + } + + public get contextValue() { + return this.message.type === TestMessageType.Error ? this.message.contextValue : undefined; + } + + constructor(public readonly result: ITestResult, test: TestResultItem, public readonly taskIndex: number, public readonly messageIndex: number) { + this.test = test.item; + const messages = test.tasks[taskIndex].messages; + this.messageIndex = messageIndex; + + const parts = { messageIndex, resultId: result.id, taskIndex, testExtId: test.item.extId }; + this.expectedUri = buildTestUri({ ...parts, type: TestUriType.ResultExpectedOutput }); + this.actualUri = buildTestUri({ ...parts, type: TestUriType.ResultActualOutput }); + this.messageUri = buildTestUri({ ...parts, type: TestUriType.ResultMessage }); + + const message = this.message = messages[this.messageIndex]; + this.context = getMessageArgs(test, message); + this.revealLocation = message.location ?? (test.item.uri && test.item.range ? { uri: test.item.uri, range: Range.lift(test.item.range) } : undefined); + } +} + +export class TaskSubject { + public readonly outputUri: URI; + public readonly revealLocation: undefined; + + constructor(public readonly result: ITestResult, public readonly taskIndex: number) { + this.outputUri = buildTestUri({ resultId: result.id, taskIndex, type: TestUriType.TaskOutput }); + } +} + +export class TestOutputSubject { + public readonly outputUri: URI; + public readonly revealLocation: undefined; + public readonly task: ITestRunTask; + + constructor(public readonly result: ITestResult, public readonly taskIndex: number, public readonly test: TestResultItem) { + this.outputUri = buildTestUri({ resultId: this.result.id, taskIndex: this.taskIndex, testExtId: this.test.item.extId, type: TestUriType.TestOutput }); + this.task = result.tasks[this.taskIndex]; + } +} + +export type InspectSubject = MessageSubject | TaskSubject | TestOutputSubject; + +export const equalsSubject = (a: InspectSubject, b: InspectSubject) => ( + (a instanceof MessageSubject && b instanceof MessageSubject && a.message === b.message) || + (a instanceof TaskSubject && b instanceof TaskSubject && a.result === b.result && a.taskIndex === b.taskIndex) || + (a instanceof TestOutputSubject && b instanceof TestOutputSubject && a.test === b.test && a.taskIndex === b.taskIndex) +); + + +export const mapFindTestMessage = (test: TestResultItem, fn: (task: ITestTaskState, message: ITestMessage, messageIndex: number, taskIndex: number) => T | undefined) => { + for (let taskIndex = 0; taskIndex < test.tasks.length; taskIndex++) { + const task = test.tasks[taskIndex]; + for (let messageIndex = 0; messageIndex < task.messages.length; messageIndex++) { + const r = fn(task, task.messages[messageIndex], messageIndex, taskIndex); + if (r !== undefined) { + return r; + } + } + } + + return undefined; +}; diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts new file mode 100644 index 00000000000..f6aa368c619 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts @@ -0,0 +1,853 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; +import { ITreeContextMenuEvent, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; +import { Emitter, Event } from 'vs/base/common/event'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { Iterable } from 'vs/base/common/iterator'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { MarshalledId } from 'vs/base/common/marshallingIds'; +import { autorun } from 'vs/base/common/observable'; +import { count } from 'vs/base/common/strings'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { isDefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; +import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay'; +import * as icons from 'vs/workbench/contrib/testing/browser/icons'; +import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { TestOutputSubject, InspectSubject, TaskSubject, MessageSubject, mapFindTestMessage, getMessageArgs } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { Testing } from 'vs/workbench/contrib/testing/common/constants'; +import { ITestCoverageService } from 'vs/workbench/contrib/testing/common/testCoverageService'; +import { ITestExplorerFilterState } from 'vs/workbench/contrib/testing/common/testExplorerFilterState'; +import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; +import { ITestResult, ITestRunTaskResults, LiveTestResult, TestResultItemChangeReason, maxCountPriority } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { IRichLocation, ITestItemContext, ITestMessage, ITestMessageMenuArgs, InternalTestItem, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset, testResultStateToContextValues } from 'vs/workbench/contrib/testing/common/testTypes'; +import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; +import { cmpPriority } from 'vs/workbench/contrib/testing/common/testingStates'; +import { TestUriType, buildTestUri } from 'vs/workbench/contrib/testing/common/testingUri'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + + +interface ITreeElement { + type: string; + context: unknown; + id: string; + label: string; + onDidChange: Event; + labelWithIcons?: readonly (HTMLSpanElement | string)[]; + icon?: ThemeIcon; + description?: string; + ariaLabel?: string; +} + +interface ITreeElement { + type: string; + context: unknown; + id: string; + label: string; + onDidChange: Event; + labelWithIcons?: readonly (HTMLSpanElement | string)[]; + icon?: ThemeIcon; + description?: string; + ariaLabel?: string; +} + +class TestResultElement implements ITreeElement { + public readonly changeEmitter = new Emitter(); + public readonly onDidChange = this.changeEmitter.event; + public readonly type = 'result'; + public readonly context = this.value.id; + public readonly id = this.value.id; + public readonly label = this.value.name; + + public get icon() { + return icons.testingStatesToIcons.get( + this.value.completedAt === undefined + ? TestResultState.Running + : maxCountPriority(this.value.counts) + ); + } + + constructor(public readonly value: ITestResult) { } +} + +const openCoverageLabel = localize('openTestCoverage', 'View Test Coverage'); +const closeCoverageLabel = localize('closeTestCoverage', 'Close Test Coverage'); + +class CoverageElement implements ITreeElement { + public readonly type = 'coverage'; + public readonly context: undefined; + public readonly id = `coverage-${this.results.id}/${this.task.id}`; + public readonly onDidChange: Event; + + public get label() { + return this.isOpen ? closeCoverageLabel : openCoverageLabel; + } + + public get icon() { + return this.isOpen ? widgetClose : icons.testingCoverageReport; + } + + public get isOpen() { + return this.coverageService.selected.get()?.fromTaskId === this.task.id; + } + + constructor( + private readonly results: ITestResult, + public readonly task: ITestRunTaskResults, + private readonly coverageService: ITestCoverageService, + ) { + this.onDidChange = Event.fromObservableLight(coverageService.selected); + } + +} + +class TestCaseElement implements ITreeElement { + public readonly type = 'test'; + public readonly context: ITestItemContext = { + $mid: MarshalledId.TestItemContext, + tests: [InternalTestItem.serialize(this.test)], + }; + public readonly id = `${this.results.id}/${this.test.item.extId}`; + public readonly description?: string; + + public get onDidChange() { + if (!(this.results instanceof LiveTestResult)) { + return Event.None; + } + + return Event.filter(this.results.onChange, e => e.item.item.extId === this.test.item.extId); + } + + public get state() { + return this.test.tasks[this.taskIndex].state; + } + + public get label() { + return this.test.item.label; + } + + public get labelWithIcons() { + return renderLabelWithIcons(this.label); + } + + public get icon() { + return icons.testingStatesToIcons.get(this.state); + } + + public get outputSubject() { + return new TestOutputSubject(this.results, this.taskIndex, this.test); + } + + + constructor( + public readonly results: ITestResult, + public readonly test: TestResultItem, + public readonly taskIndex: number, + ) { } +} + +class TaskElement implements ITreeElement { + public readonly changeEmitter = new Emitter(); + public readonly onDidChange = this.changeEmitter.event; + public readonly type = 'task'; + public readonly context: string; + public readonly id: string; + public readonly label: string; + public readonly itemsCache = new CreationCache(); + + public get icon() { + return this.results.tasks[this.index].running ? icons.testingStatesToIcons.get(TestResultState.Running) : undefined; + } + + constructor(public readonly results: ITestResult, public readonly task: ITestRunTaskResults, public readonly index: number) { + this.id = `${results.id}/${index}`; + this.task = results.tasks[index]; + this.context = String(index); + this.label = this.task.name ?? localize('testUnnamedTask', 'Unnamed Task'); + } +} + +class TestMessageElement implements ITreeElement { + public readonly type = 'message'; + public readonly id: string; + public readonly label: string; + public readonly uri: URI; + public readonly location?: IRichLocation; + public readonly description?: string; + public readonly contextValue?: string; + public readonly message: ITestMessage; + + public get onDidChange() { + if (!(this.result instanceof LiveTestResult)) { + return Event.None; + } + + // rerender when the test case changes so it gets retired events + return Event.filter(this.result.onChange, e => e.item.item.extId === this.test.item.extId); + } + + public get context(): ITestMessageMenuArgs { + return getMessageArgs(this.test, this.message); + } + + public get outputSubject() { + return new TestOutputSubject(this.result, this.taskIndex, this.test); + } + + constructor( + public readonly result: ITestResult, + public readonly test: TestResultItem, + public readonly taskIndex: number, + public readonly messageIndex: number, + ) { + const m = this.message = test.tasks[taskIndex].messages[messageIndex]; + + this.location = m.location; + this.contextValue = m.type === TestMessageType.Error ? m.contextValue : undefined; + this.uri = buildTestUri({ + type: TestUriType.ResultMessage, + messageIndex, + resultId: result.id, + taskIndex, + testExtId: test.item.extId + }); + + this.id = this.uri.toString(); + + const asPlaintext = renderTestMessageAsText(m.message); + const lines = count(asPlaintext.trimEnd(), '\n'); + this.label = firstLine(asPlaintext); + if (lines > 0) { + this.description = lines > 1 + ? localize('messageMoreLinesN', '+ {0} more lines', lines) + : localize('messageMoreLines1', '+ 1 more line'); + } + } +} + +type TreeElement = TestResultElement | TestCaseElement | TestMessageElement | TaskElement | CoverageElement; + +export class OutputPeekTree extends Disposable { + private disposed = false; + private readonly tree: WorkbenchCompressibleObjectTree; + private readonly treeActions: TreeActionsProvider; + private readonly requestReveal = this._register(new Emitter()); + + public readonly onDidRequestReview = this.requestReveal.event; + + constructor( + container: HTMLElement, + onDidReveal: Event<{ subject: InspectSubject; preserveFocus: boolean }>, + options: { showRevealLocationOnMessages: boolean; locationForProgress: string }, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @ITestResultService results: ITestResultService, + @IInstantiationService instantiationService: IInstantiationService, + @ITestExplorerFilterState explorerFilter: ITestExplorerFilterState, + @ITestCoverageService coverageService: ITestCoverageService, + @IProgressService progressService: IProgressService, + ) { + super(); + + this.treeActions = instantiationService.createInstance(TreeActionsProvider, options.showRevealLocationOnMessages, this.requestReveal,); + const diffIdentityProvider: IIdentityProvider = { + getId(e: TreeElement) { + return e.id; + } + }; + + this.tree = this._register(instantiationService.createInstance( + WorkbenchCompressibleObjectTree, + 'Test Output Peek', + container, + { + getHeight: () => 22, + getTemplateId: () => TestRunElementRenderer.ID, + }, + [instantiationService.createInstance(TestRunElementRenderer, this.treeActions)], + { + compressionEnabled: true, + hideTwistiesOfChildlessElements: true, + identityProvider: diffIdentityProvider, + sorter: { + compare(a, b) { + if (a instanceof TestCaseElement && b instanceof TestCaseElement) { + return cmpPriority(a.state, b.state); + } + + return 0; + }, + }, + accessibilityProvider: { + getAriaLabel(element: ITreeElement) { + return element.ariaLabel || element.label; + }, + getWidgetAriaLabel() { + return localize('testingPeekLabel', 'Test Result Messages'); + } + } + }, + )) as WorkbenchCompressibleObjectTree; + + const cc = new CreationCache(); + const getTaskChildren = (taskElem: TaskElement): Iterable> => { + const { results, index, itemsCache, task } = taskElem; + const tests = Iterable.filter(results.tests, test => test.tasks[index].state >= TestResultState.Running || test.tasks[index].messages.length > 0); + let result: Iterable> = Iterable.map(tests, test => ({ + element: itemsCache.getOrCreate(test, () => new TestCaseElement(results, test, index)), + incompressible: true, + children: getTestChildren(results, test, index), + })); + + if (task.coverage.get()) { + result = Iterable.concat( + Iterable.single>({ + element: new CoverageElement(results, task, coverageService), + collapsible: true, + incompressible: true, + }), + result, + ); + } + + return result; + }; + + const getTestChildren = (result: ITestResult, test: TestResultItem, taskIndex: number): Iterable> => { + return test.tasks[taskIndex].messages + .map((m, messageIndex) => + m.type === TestMessageType.Error + ? { element: cc.getOrCreate(m, () => new TestMessageElement(result, test, taskIndex, messageIndex)), incompressible: false } + : undefined + ) + .filter(isDefined); + }; + + const getResultChildren = (result: ITestResult): Iterable> => { + return result.tasks.map((task, taskIndex) => { + const taskElem = cc.getOrCreate(task, () => new TaskElement(result, task, taskIndex)); + return ({ + element: taskElem, + incompressible: false, + collapsible: true, + children: getTaskChildren(taskElem), + }); + }); + }; + + const getRootChildren = () => results.results.map(result => { + const element = cc.getOrCreate(result, () => new TestResultElement(result)); + return { + element, + incompressible: true, + collapsible: true, + collapsed: this.tree.hasElement(element) ? this.tree.isCollapsed(element) : true, + children: getResultChildren(result) + }; + }); + + // Queued result updates to prevent spamming CPU when lots of tests are + // completing and messaging quickly (#142514) + const taskChildrenToUpdate = new Set(); + const taskChildrenUpdate = this._register(new RunOnceScheduler(() => { + for (const taskNode of taskChildrenToUpdate) { + if (this.tree.hasElement(taskNode)) { + this.tree.setChildren(taskNode, getTaskChildren(taskNode), { diffIdentityProvider }); + } + } + taskChildrenToUpdate.clear(); + }, 300)); + + const queueTaskChildrenUpdate = (taskNode: TaskElement) => { + taskChildrenToUpdate.add(taskNode); + if (!taskChildrenUpdate.isScheduled()) { + taskChildrenUpdate.schedule(); + } + }; + + const attachToResults = (result: LiveTestResult) => { + const resultNode = cc.get(result)! as TestResultElement; + const disposable = new DisposableStore(); + disposable.add(result.onNewTask(i => { + if (result.tasks.length === 1) { + this.requestReveal.fire(new TaskSubject(result, 0)); // reveal the first task in new runs + } + + if (this.tree.hasElement(resultNode)) { + this.tree.setChildren(resultNode, getResultChildren(result), { diffIdentityProvider }); + } + + // note: tasks are bounded and their lifetime is equivalent to that of + // the test result, so this doesn't leak indefinitely. + const task = result.tasks[i]; + disposable.add(autorun(reader => { + task.coverage.read(reader); // add it to the autorun + queueTaskChildrenUpdate(cc.get(task) as TaskElement); + })); + })); + disposable.add(result.onEndTask(index => { + (cc.get(result.tasks[index]) as TaskElement | undefined)?.changeEmitter.fire(); + })); + + disposable.add(result.onChange(e => { + // try updating the item in each of its tasks + for (const [index, task] of result.tasks.entries()) { + const taskNode = cc.get(task) as TaskElement; + if (!this.tree.hasElement(taskNode)) { + continue; + } + + const itemNode = taskNode.itemsCache.get(e.item); + if (itemNode && this.tree.hasElement(itemNode)) { + if (e.reason === TestResultItemChangeReason.NewMessage && e.message.type === TestMessageType.Error) { + this.tree.setChildren(itemNode, getTestChildren(result, e.item, index), { diffIdentityProvider }); + } + return; + } + + queueTaskChildrenUpdate(taskNode); + } + })); + + disposable.add(result.onComplete(() => { + resultNode.changeEmitter.fire(); + disposable.dispose(); + })); + + return resultNode; + }; + + this._register(results.onResultsChanged(e => { + // little hack here: a result change can cause the peek to be disposed, + // but this listener will still be queued. Doing stuff with the tree + // will cause errors. + if (this.disposed) { + return; + } + + if ('completed' in e) { + (cc.get(e.completed) as TestResultElement | undefined)?.changeEmitter.fire(); + return; + } + + this.tree.setChildren(null, getRootChildren(), { diffIdentityProvider }); + + // done after setChildren intentionally so that the ResultElement exists in the cache. + if ('started' in e) { + for (const child of this.tree.getNode(null).children) { + this.tree.collapse(child.element, false); + } + + this.tree.expand(attachToResults(e.started), true); + } + })); + + const revealItem = (element: TreeElement, preserveFocus: boolean) => { + this.tree.setFocus([element]); + this.tree.setSelection([element]); + if (!preserveFocus) { + this.tree.domFocus(); + } + }; + + this._register(onDidReveal(async ({ subject, preserveFocus = false }) => { + if (subject instanceof TaskSubject) { + const resultItem = this.tree.getNode(null).children.find(c => { + if (c.element instanceof TaskElement) { + return c.element.results.id === subject.result.id && c.element.index === subject.taskIndex; + } + if (c.element instanceof TestResultElement) { + return c.element.id === subject.result.id; + } + return false; + }); + + if (resultItem) { + revealItem(resultItem.element!, preserveFocus); + } + return; + } + + const revealElement = subject instanceof TestOutputSubject + ? cc.get(subject.task)?.itemsCache.get(subject.test) + : cc.get(subject.message); + if (!revealElement || !this.tree.hasElement(revealElement)) { + return; + } + + const parents: TreeElement[] = []; + for (let parent = this.tree.getParentElement(revealElement); parent; parent = this.tree.getParentElement(parent)) { + parents.unshift(parent); + } + + for (const parent of parents) { + this.tree.expand(parent); + } + + if (this.tree.getRelativeTop(revealElement) === null) { + this.tree.reveal(revealElement, 0.5); + } + + revealItem(revealElement, preserveFocus); + })); + + this._register(this.tree.onDidOpen(async e => { + if (e.element instanceof TestMessageElement) { + this.requestReveal.fire(new MessageSubject(e.element.result, e.element.test, e.element.taskIndex, e.element.messageIndex)); + } else if (e.element instanceof TestCaseElement) { + const t = e.element; + const message = mapFindTestMessage(e.element.test, (_t, _m, mesasgeIndex, taskIndex) => + new MessageSubject(t.results, t.test, taskIndex, mesasgeIndex)); + this.requestReveal.fire(message || new TestOutputSubject(t.results, 0, t.test)); + } else if (e.element instanceof CoverageElement) { + const task = e.element.task; + if (e.element.isOpen) { + return coverageService.closeCoverage(); + } + progressService.withProgress( + { location: options.locationForProgress }, + () => coverageService.openCoverage(task, true) + ); + } + })); + + this._register(this.tree.onDidChangeSelection(evt => { + for (const element of evt.elements) { + if (element && 'test' in element) { + explorerFilter.reveal.value = element.test.item.extId; + break; + } + } + })); + + + this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); + + this.tree.setChildren(null, getRootChildren()); + for (const result of results.results) { + if (!result.completedAt && result instanceof LiveTestResult) { + attachToResults(result); + } + } + } + + public layout(height: number, width: number) { + this.tree.layout(height, width); + } + + private onContextMenu(evt: ITreeContextMenuEvent) { + if (!evt.element) { + return; + } + + const actions = this.treeActions.provideActionBar(evt.element); + this.contextMenuService.showContextMenu({ + getAnchor: () => evt.anchor, + getActions: () => actions.secondary.length + ? [...actions.primary, new Separator(), ...actions.secondary] + : actions.primary, + getActionsContext: () => evt.element?.context + }); + } + + public override dispose() { + super.dispose(); + this.disposed = true; + } +} + +interface TemplateData { + label: HTMLElement; + icon: HTMLElement; + actionBar: ActionBar; + elementDisposable: DisposableStore; + templateDisposable: DisposableStore; +} + +class TestRunElementRenderer implements ICompressibleTreeRenderer { + public static readonly ID = 'testRunElementRenderer'; + public readonly templateId = TestRunElementRenderer.ID; + + constructor( + private readonly treeActions: TreeActionsProvider, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { } + + /** @inheritdoc */ + public renderCompressedElements(node: ITreeNode, FuzzyScore>, _index: number, templateData: TemplateData): void { + const chain = node.element.elements; + const lastElement = chain[chain.length - 1]; + if ((lastElement instanceof TaskElement || lastElement instanceof TestMessageElement) && chain.length >= 2) { + this.doRender(chain[chain.length - 2], templateData, lastElement); + } else { + this.doRender(lastElement, templateData); + } + } + + /** @inheritdoc */ + public renderTemplate(container: HTMLElement): TemplateData { + const templateDisposable = new DisposableStore(); + const wrapper = dom.append(container, dom.$('.test-peek-item')); + const icon = dom.append(wrapper, dom.$('.state')); + const label = dom.append(wrapper, dom.$('.name')); + + const actionBar = new ActionBar(wrapper, { + actionViewItemProvider: (action, options) => + action instanceof MenuItemAction + ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) + : undefined + }); + + const elementDisposable = new DisposableStore(); + templateDisposable.add(elementDisposable); + templateDisposable.add(actionBar); + + return { + icon, + label, + actionBar, + elementDisposable, + templateDisposable, + }; + } + + /** @inheritdoc */ + public renderElement(element: ITreeNode, _index: number, templateData: TemplateData): void { + this.doRender(element.element, templateData); + } + + /** @inheritdoc */ + public disposeTemplate(templateData: TemplateData): void { + templateData.templateDisposable.dispose(); + } + + /** Called to render a new element */ + private doRender(element: ITreeElement, templateData: TemplateData, subjectElement?: ITreeElement) { + templateData.elementDisposable.clear(); + templateData.elementDisposable.add( + element.onDidChange(() => this.doRender(element, templateData, subjectElement)), + ); + this.doRenderInner(element, templateData, subjectElement); + } + + /** Called, and may be re-called, to render or re-render an element */ + private doRenderInner(element: ITreeElement, templateData: TemplateData, subjectElement: ITreeElement | undefined) { + let { label, labelWithIcons, description } = element; + if (subjectElement instanceof TestMessageElement) { + description = subjectElement.label; + } + + const descriptionElement = description ? dom.$('span.test-label-description', {}, description) : ''; + if (labelWithIcons) { + dom.reset(templateData.label, ...labelWithIcons, descriptionElement); + } else { + dom.reset(templateData.label, label, descriptionElement); + } + + const icon = element.icon; + templateData.icon.className = `computed-state ${icon ? ThemeIcon.asClassName(icon) : ''}`; + + const actions = this.treeActions.provideActionBar(element); + templateData.actionBar.clear(); + templateData.actionBar.context = element.context; + templateData.actionBar.push(actions.primary, { icon: true, label: false }); + } +} + +class TreeActionsProvider { + constructor( + private readonly showRevealLocationOnMessages: boolean, + private readonly requestReveal: Emitter, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IMenuService private readonly menuService: IMenuService, + @ICommandService private readonly commandService: ICommandService, + @ITestProfileService private readonly testProfileService: ITestProfileService, + @IEditorService private readonly editorService: IEditorService, + ) { } + + public provideActionBar(element: ITreeElement) { + const test = element instanceof TestCaseElement ? element.test : undefined; + const capabilities = test ? this.testProfileService.capabilitiesForTest(test) : 0; + + const contextKeys: [string, unknown][] = [ + ['peek', Testing.OutputPeekContributionId], + [TestingContextKeys.peekItemType.key, element.type], + ]; + + let id = MenuId.TestPeekElement; + const primary: IAction[] = []; + const secondary: IAction[] = []; + + if (element instanceof TaskElement) { + primary.push(new Action( + 'testing.outputPeek.showResultOutput', + localize('testing.showResultOutput', "Show Result Output"), + ThemeIcon.asClassName(Codicon.terminal), + undefined, + () => this.requestReveal.fire(new TaskSubject(element.results, element.index)), + )); + } + + if (element instanceof TestResultElement) { + // only show if there are no collapsed test nodes that have more specific choices + if (element.value.tasks.length === 1) { + primary.push(new Action( + 'testing.outputPeek.showResultOutput', + localize('testing.showResultOutput', "Show Result Output"), + ThemeIcon.asClassName(Codicon.terminal), + undefined, + () => this.requestReveal.fire(new TaskSubject(element.value, 0)), + )); + } + + primary.push(new Action( + 'testing.outputPeek.reRunLastRun', + localize('testing.reRunLastRun', "Rerun Test Run"), + ThemeIcon.asClassName(icons.testingRunIcon), + undefined, + () => this.commandService.executeCommand('testing.reRunLastRun', element.value.id), + )); + + if (capabilities & TestRunProfileBitset.Debug) { + primary.push(new Action( + 'testing.outputPeek.debugLastRun', + localize('testing.debugLastRun', "Debug Test Run"), + ThemeIcon.asClassName(icons.testingDebugIcon), + undefined, + () => this.commandService.executeCommand('testing.debugLastRun', element.value.id), + )); + } + } + + if (element instanceof TestCaseElement || element instanceof TestMessageElement) { + contextKeys.push( + [TestingContextKeys.testResultOutdated.key, element.test.retired], + [TestingContextKeys.testResultState.key, testResultStateToContextValues[element.test.ownComputedState]], + ...getTestItemContextOverlay(element.test, capabilities), + ); + + const extId = element.test.item.extId; + if (element.test.tasks[element.taskIndex].messages.some(m => m.type === TestMessageType.Output)) { + primary.push(new Action( + 'testing.outputPeek.showResultOutput', + localize('testing.showResultOutput', "Show Result Output"), + ThemeIcon.asClassName(Codicon.terminal), + undefined, + () => this.requestReveal.fire(element.outputSubject), + )); + } + + secondary.push(new Action( + 'testing.outputPeek.revealInExplorer', + localize('testing.revealInExplorer', "Reveal in Test Explorer"), + ThemeIcon.asClassName(Codicon.listTree), + undefined, + () => this.commandService.executeCommand('_revealTestInExplorer', extId), + )); + + if (capabilities & TestRunProfileBitset.Run) { + primary.push(new Action( + 'testing.outputPeek.runTest', + localize('run test', 'Run Test'), + ThemeIcon.asClassName(icons.testingRunIcon), + undefined, + () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Run, extId), + )); + } + + if (capabilities & TestRunProfileBitset.Debug) { + primary.push(new Action( + 'testing.outputPeek.debugTest', + localize('debug test', 'Debug Test'), + ThemeIcon.asClassName(icons.testingDebugIcon), + undefined, + () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Debug, extId), + )); + } + + } + + if (element instanceof TestMessageElement) { + primary.push(new Action( + 'testing.outputPeek.goToFile', + localize('testing.goToFile', "Go to Source"), + ThemeIcon.asClassName(Codicon.goToFile), + undefined, + () => this.commandService.executeCommand('vscode.revealTest', element.test.item.extId), + )); + } + + if (element instanceof TestMessageElement) { + id = MenuId.TestMessageContext; + contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]); + if (this.showRevealLocationOnMessages && element.location) { + primary.push(new Action( + 'testing.outputPeek.goToError', + localize('testing.goToError', "Go to Source"), + ThemeIcon.asClassName(Codicon.goToFile), + undefined, + () => this.editorService.openEditor({ + resource: element.location!.uri, + options: { + selection: element.location!.range, + preserveFocus: true, + } + }), + )); + } + } + + + const contextOverlay = this.contextKeyService.createOverlay(contextKeys); + const result = { primary, secondary }; + const menu = this.menuService.getMenuActions(id, contextOverlay, { arg: element.context }); + createAndFillInActionBarActions(menu, result, 'inline'); + return result; + } +} + +class CreationCache { + private readonly v = new WeakMap(); + + public get(key: object): T2 | undefined { + return this.v.get(key) as T2 | undefined; + } + + public getOrCreate(ref: object, factory: () => T2): T2 { + const existing = this.v.get(ref); + if (existing) { + return existing as T2; + } + + const fresh = factory(); + this.v.set(ref, fresh); + return fresh; + } +} + +const firstLine = (str: string) => { + const index = str.indexOf('\n'); + return index === -1 ? str : str.slice(0, index); +}; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index ec21581a9a6..0012ae4a1c5 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -5,56 +5,39 @@ import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; -import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; -import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; -import { ITreeContextMenuEvent, ITreeNode } from 'vs/base/browser/ui/tree/tree'; -import { Action, IAction, Separator } from 'vs/base/common/actions'; -import { Delayer, Limiter, RunOnceScheduler } from 'vs/base/common/async'; -import { VSBuffer } from 'vs/base/common/buffer'; +import { IAction } from 'vs/base/common/actions'; +import { Limiter } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; -import { FuzzyScore } from 'vs/base/common/filters'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; import { stripIcons } from 'vs/base/common/iconLabels'; import { Iterable } from 'vs/base/common/iterator'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; -import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { MarshalledId } from 'vs/base/common/marshallingIds'; -import { autorun } from 'vs/base/common/observable'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { count } from 'vs/base/common/strings'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./testingOutputPeek'; -import { ICodeEditor, IDiffEditorConstructionOptions, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; -import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; -import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditor, IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IPeekViewService, PeekViewWidget, peekViewResultsBackground, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IPeekViewService, PeekViewWidget, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView'; import { localize, localize2 } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { FloatingClickMenu } from 'vs/platform/actions/browser/floatingMenu'; -import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { Action2, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { Action2, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -65,116 +48,35 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IProgressService } from 'vs/platform/progress/common/progress'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; -import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; -import { EditorModel } from 'vs/workbench/common/editor/editorModel'; -import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; -import { DetachedProcessInfo } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; -import { IDetachedTerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; -import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay'; -import * as icons from 'vs/workbench/contrib/testing/browser/icons'; -import { colorizeTestMessageInEditor, renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput'; +import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, equalsSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { OutputPeekTree } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree'; import { testingMessagePeekBorder, testingPeekBorder, testingPeekHeaderBackground, testingPeekMessageHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme'; import { AutoOpenPeekViewWhen, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; import { IObservableValue, MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; -import { ITestCoverageService } from 'vs/workbench/contrib/testing/common/testCoverageService'; -import { ITestExplorerFilterState } from 'vs/workbench/contrib/testing/common/testExplorerFilterState'; -import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; -import { ITaskRawOutput, ITestResult, ITestRunTaskResults, LiveTestResult, TestResultItemChange, TestResultItemChangeReason, maxCountPriority, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestResult, LiveTestResult, TestResultItemChange, TestResultItemChangeReason, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService, ResultChangeEvent } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestFollowup, ITestService } from 'vs/workbench/contrib/testing/common/testService'; -import { IRichLocation, ITestErrorMessage, ITestItem, ITestItemContext, ITestMessage, ITestMessageMenuArgs, ITestRunTask, ITestTaskState, InternalTestItem, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset, getMarkId, testResultStateToContextValues } from 'vs/workbench/contrib/testing/common/testTypes'; +import { IRichLocation, ITestMessage, TestMessageType, TestResultItem } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { IShowResultOptions, ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener'; -import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; +import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; import { ParsedTestUri, TestUriType, buildTestUri, parseTestUri } from 'vs/workbench/contrib/testing/common/testingUri'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; -const getMessageArgs = (test: TestResultItem, message: ITestMessage): ITestMessageMenuArgs => ({ - $mid: MarshalledId.TestMessageMenuArgs, - test: InternalTestItem.serialize(test), - message: ITestMessage.serialize(message), -}); - -class MessageSubject { - public readonly test: ITestItem; - public readonly message: ITestMessage; - public readonly expectedUri: URI; - public readonly actualUri: URI; - public readonly messageUri: URI; - public readonly revealLocation: IRichLocation | undefined; - public readonly context: ITestMessageMenuArgs | undefined; - - public get isDiffable() { - return this.message.type === TestMessageType.Error && isDiffable(this.message); - } - - public get contextValue() { - return this.message.type === TestMessageType.Error ? this.message.contextValue : undefined; - } - - constructor(public readonly result: ITestResult, test: TestResultItem, public readonly taskIndex: number, public readonly messageIndex: number) { - this.test = test.item; - const messages = test.tasks[taskIndex].messages; - this.messageIndex = messageIndex; - - const parts = { messageIndex, resultId: result.id, taskIndex, testExtId: test.item.extId }; - this.expectedUri = buildTestUri({ ...parts, type: TestUriType.ResultExpectedOutput }); - this.actualUri = buildTestUri({ ...parts, type: TestUriType.ResultActualOutput }); - this.messageUri = buildTestUri({ ...parts, type: TestUriType.ResultMessage }); - - const message = this.message = messages[this.messageIndex]; - this.context = getMessageArgs(test, message); - this.revealLocation = message.location ?? (test.item.uri && test.item.range ? { uri: test.item.uri, range: Range.lift(test.item.range) } : undefined); - } -} - -class TaskSubject { - public readonly outputUri: URI; - public readonly revealLocation: undefined; - - constructor(public readonly result: ITestResult, public readonly taskIndex: number) { - this.outputUri = buildTestUri({ resultId: result.id, taskIndex, type: TestUriType.TaskOutput }); - } -} - -class TestOutputSubject { - public readonly outputUri: URI; - public readonly revealLocation: undefined; - public readonly task: ITestRunTask; - - constructor(public readonly result: ITestResult, public readonly taskIndex: number, public readonly test: TestResultItem) { - this.outputUri = buildTestUri({ resultId: this.result.id, taskIndex: this.taskIndex, testExtId: this.test.item.extId, type: TestUriType.TestOutput }); - this.task = result.tasks[this.taskIndex]; - } -} - -type InspectSubject = MessageSubject | TaskSubject | TestOutputSubject; - -const equalsSubject = (a: InspectSubject, b: InspectSubject) => ( - (a instanceof MessageSubject && b instanceof MessageSubject && a.message === b.message) || - (a instanceof TaskSubject && b instanceof TaskSubject && a.result === b.result && a.taskIndex === b.taskIndex) || - (a instanceof TestOutputSubject && b instanceof TestOutputSubject && a.test === b.test && a.taskIndex === b.taskIndex) -); /** Iterates through every message in every result */ function* allMessages(results: readonly ITestResult[]) { @@ -501,19 +403,6 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener } } -const mapFindTestMessage = (test: TestResultItem, fn: (task: ITestTaskState, message: ITestMessage, messageIndex: number, taskIndex: number) => T | undefined) => { - for (let taskIndex = 0; taskIndex < test.tasks.length; taskIndex++) { - const task = test.tasks[taskIndex]; - for (let messageIndex = 0; messageIndex < task.messages.length; messageIndex++) { - const r = fn(task, task.messages[messageIndex], messageIndex, taskIndex); - if (r !== undefined) { - return r; - } - } - } - - return undefined; -}; /** * Adds output/message peek functionality to code editors. @@ -1245,491 +1134,8 @@ export class TestResultsView extends ViewPane { } } -interface IPeekOutputRenderer extends IDisposable { - /** Updates the displayed test. Should clear if it cannot display the test. */ - update(subject: InspectSubject): void; - /** Recalculate content layout. */ - layout(dimension: dom.IDimension): void; - /** Dispose the content provider. */ - dispose(): void; -} - -const commonEditorOptions: IEditorOptions = { - scrollBeyondLastLine: false, - links: true, - lineNumbers: 'off', - scrollbar: { - verticalScrollbarSize: 14, - horizontal: 'auto', - useShadows: true, - verticalHasArrows: false, - horizontalHasArrows: false, - alwaysConsumeMouseWheel: false - }, - fixedOverflowWidgets: true, - readOnly: true, - minimap: { - enabled: false - }, - wordWrap: 'on', -}; - -const diffEditorOptions: IDiffEditorConstructionOptions = { - ...commonEditorOptions, - enableSplitViewResizing: true, - isInEmbeddedEditor: true, - renderOverviewRuler: false, - ignoreTrimWhitespace: false, - renderSideBySide: true, - useInlineViewWhenSpaceIsLimited: false, - originalAriaLabel: localize('testingOutputExpected', 'Expected result'), - modifiedAriaLabel: localize('testingOutputActual', 'Actual result'), - diffAlgorithm: 'advanced', -}; - -const isDiffable = (message: ITestMessage): message is ITestErrorMessage & { actual: string; expected: string } => - message.type === TestMessageType.Error && message.actual !== undefined && message.expected !== undefined; - -class DiffContentProvider extends Disposable implements IPeekOutputRenderer { - private readonly widget = this._register(new MutableDisposable()); - private readonly model = this._register(new MutableDisposable()); - private dimension?: dom.IDimension; - - constructor( - private readonly editor: ICodeEditor | undefined, - private readonly container: HTMLElement, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextModelService private readonly modelService: ITextModelService, - ) { - super(); - } - - public async update(subject: InspectSubject) { - if (!(subject instanceof MessageSubject)) { - return this.clear(); - } - const message = subject.message; - if (!isDiffable(message)) { - return this.clear(); - } - - const [original, modified] = await Promise.all([ - this.modelService.createModelReference(subject.expectedUri), - this.modelService.createModelReference(subject.actualUri), - ]); - - const model = this.model.value = new SimpleDiffEditorModel(original, modified); - if (!this.widget.value) { - this.widget.value = this.editor ? this.instantiationService.createInstance( - EmbeddedDiffEditorWidget, - this.container, - diffEditorOptions, - {}, - this.editor, - ) : this.instantiationService.createInstance( - DiffEditorWidget, - this.container, - diffEditorOptions, - {}, - ); - - if (this.dimension) { - this.widget.value.layout(this.dimension); - } - } - - this.widget.value.setModel(model); - this.widget.value.updateOptions(this.getOptions( - isMultiline(message.expected) || isMultiline(message.actual) - )); - } - - private clear() { - this.model.clear(); - this.widget.clear(); - } - - public layout(dimensions: dom.IDimension) { - this.dimension = dimensions; - this.widget.value?.layout(dimensions); - } - - protected getOptions(isMultiline: boolean): IDiffEditorOptions { - return isMultiline - ? { ...diffEditorOptions, lineNumbers: 'on' } - : { ...diffEditorOptions, lineNumbers: 'off' }; - } -} - -class ScrollableMarkdownMessage extends Disposable { - private readonly scrollable: DomScrollableElement; - private readonly element: HTMLElement; - - constructor(container: HTMLElement, markdown: MarkdownRenderer, message: IMarkdownString) { - super(); - - const rendered = this._register(markdown.render(message, {})); - rendered.element.style.height = '100%'; - rendered.element.style.userSelect = 'text'; - container.appendChild(rendered.element); - this.element = rendered.element; - - this.scrollable = this._register(new DomScrollableElement(rendered.element, { - className: 'preview-text', - })); - container.appendChild(this.scrollable.getDomNode()); - - this._register(toDisposable(() => { - this.scrollable.getDomNode().remove(); - })); - - this.scrollable.scanDomNode(); - } - - public layout(height: number, width: number) { - // Remove padding of `.monaco-editor .zone-widget.test-output-peek .preview-text` - this.scrollable.setScrollDimensions({ - width: width - 32, - height: height - 16, - scrollWidth: this.element.scrollWidth, - scrollHeight: this.element.scrollHeight - }); - } -} - -class MarkdownTestMessagePeek extends Disposable implements IPeekOutputRenderer { - private readonly markdown = new Lazy( - () => this._register(this.instantiationService.createInstance(MarkdownRenderer, {})), - ); - - private readonly textPreview = this._register(new MutableDisposable()); - - constructor(private readonly container: HTMLElement, @IInstantiationService private readonly instantiationService: IInstantiationService) { - super(); - } - - public update(subject: InspectSubject): void { - if (!(subject instanceof MessageSubject)) { - return this.textPreview.clear(); - } - - const message = subject.message; - if (isDiffable(message) || typeof message.message === 'string') { - return this.textPreview.clear(); - } - - this.textPreview.value = new ScrollableMarkdownMessage( - this.container, - this.markdown.value, - message.message as IMarkdownString, - ); - } - - public layout(dimension: dom.IDimension): void { - this.textPreview.value?.layout(dimension.height, dimension.width); - } -} - -class PlainTextMessagePeek extends Disposable implements IPeekOutputRenderer { - private readonly widgetDecorations = this._register(new MutableDisposable()); - private readonly widget = this._register(new MutableDisposable()); - private readonly model = this._register(new MutableDisposable()); - private dimension?: dom.IDimension; - - constructor( - private readonly editor: ICodeEditor | undefined, - private readonly container: HTMLElement, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextModelService private readonly modelService: ITextModelService, - ) { - super(); - } - - public async update(subject: InspectSubject) { - if (!(subject instanceof MessageSubject)) { - return this.clear(); - } - - const message = subject.message; - if (isDiffable(message) || message.type === TestMessageType.Output || typeof message.message !== 'string') { - return this.clear(); - } - - const modelRef = this.model.value = await this.modelService.createModelReference(subject.messageUri); - if (!this.widget.value) { - this.widget.value = this.editor ? this.instantiationService.createInstance( - EmbeddedCodeEditorWidget, - this.container, - commonEditorOptions, - {}, - this.editor, - ) : this.instantiationService.createInstance( - CodeEditorWidget, - this.container, - commonEditorOptions, - { isSimpleWidget: true } - ); - - if (this.dimension) { - this.widget.value.layout(this.dimension); - } - } - - this.widget.value.setModel(modelRef.object.textEditorModel); - this.widget.value.updateOptions(commonEditorOptions); - this.widgetDecorations.value = colorizeTestMessageInEditor(message.message, this.widget.value); - } - - private clear() { - this.widgetDecorations.clear(); - this.widget.clear(); - this.model.clear(); - } - - public layout(dimensions: dom.IDimension) { - this.dimension = dimensions; - this.widget.value?.layout(dimensions); - } -} - -class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { - private dimensions?: dom.IDimension; - private readonly terminalCwd = this._register(new MutableObservableValue('')); - private readonly xtermLayoutDelayer = this._register(new Delayer(50)); - - /** Active terminal instance. */ - private readonly terminal = this._register(new MutableDisposable()); - /** Listener for streaming result data */ - private readonly outputDataListener = this._register(new MutableDisposable()); - - constructor( - private readonly container: HTMLElement, - private readonly isInPeekView: boolean, - @ITerminalService private readonly terminalService: ITerminalService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService, - ) { - super(); - } - - private async makeTerminal() { - const prev = this.terminal.value; - if (prev) { - prev.xterm.clearBuffer(); - prev.xterm.clearSearchDecorations(); - // clearBuffer tries to retain the prompt line, but this doesn't exist for tests. - // So clear the screen (J) and move to home (H) to ensure previous data is cleaned up. - prev.xterm.write(`\x1b[2J\x1b[0;0H`); - return prev; - } - - const capabilities = new TerminalCapabilityStore(); - const cwd = this.terminalCwd; - capabilities.add(TerminalCapability.CwdDetection, { - type: TerminalCapability.CwdDetection, - get cwds() { return [cwd.value]; }, - onDidChangeCwd: cwd.onDidChange, - getCwd: () => cwd.value, - updateCwd: () => { }, - }); - - return this.terminal.value = await this.terminalService.createDetachedTerminal({ - rows: 10, - cols: 80, - readonly: true, - capabilities, - processInfo: new DetachedProcessInfo({ initialCwd: cwd.value }), - colorProvider: { - getBackgroundColor: theme => { - const terminalBackground = theme.getColor(TERMINAL_BACKGROUND_COLOR); - if (terminalBackground) { - return terminalBackground; - } - if (this.isInPeekView) { - return theme.getColor(peekViewResultsBackground); - } - const location = this.viewDescriptorService.getViewLocationById(Testing.ResultsViewId); - return location === ViewContainerLocation.Panel - ? theme.getColor(PANEL_BACKGROUND) - : theme.getColor(SIDE_BAR_BACKGROUND); - }, - } - }); - } - - public async update(subject: InspectSubject) { - this.outputDataListener.clear(); - if (subject instanceof TaskSubject) { - await this.updateForTaskSubject(subject); - } else if (subject instanceof TestOutputSubject || (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output)) { - await this.updateForTestSubject(subject); - } else { - this.clear(); - } - } - - private async updateForTestSubject(subject: TestOutputSubject | MessageSubject) { - const that = this; - const testItem = subject instanceof TestOutputSubject ? subject.test.item : subject.test; - const terminal = await this.updateGenerically({ - subject, - noOutputMessage: localize('caseNoOutput', 'The test case did not report any output.'), - getTarget: result => result?.tasks[subject.taskIndex].output, - *doInitialWrite(output, results) { - that.updateCwd(testItem.uri); - const state = subject instanceof TestOutputSubject ? subject.test : results.getStateById(testItem.extId); - if (!state) { - return; - } - - for (const message of state.tasks[subject.taskIndex].messages) { - if (message.type === TestMessageType.Output) { - yield* output.getRangeIter(message.offset, message.length); - } - } - }, - doListenForMoreData: (output, result, write) => result.onChange(e => { - if (e.reason === TestResultItemChangeReason.NewMessage && e.item.item.extId === testItem.extId && e.message.type === TestMessageType.Output) { - for (const chunk of output.getRangeIter(e.message.offset, e.message.length)) { - write(chunk.buffer); - } - } - }), - }); - - if (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output && subject.message.marker !== undefined) { - terminal?.xterm.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true); - } - } - - private updateForTaskSubject(subject: TaskSubject) { - return this.updateGenerically({ - subject, - noOutputMessage: localize('runNoOutput', 'The test run did not record any output.'), - getTarget: result => result?.tasks[subject.taskIndex], - doInitialWrite: (task, result) => { - // Update the cwd and use the first test to try to hint at the correct cwd, - // but often this will fall back to the first workspace folder. - this.updateCwd(Iterable.find(result.tests, t => !!t.item.uri)?.item.uri); - return task.output.buffers; - }, - doListenForMoreData: (task, _result, write) => task.output.onDidWriteData(e => write(e.buffer)), - }); - } - - private async updateGenerically(opts: { - subject: InspectSubject; - noOutputMessage: string; - getTarget: (result: ITestResult) => T | undefined; - doInitialWrite: (target: T, result: LiveTestResult) => Iterable; - doListenForMoreData: (target: T, result: LiveTestResult, write: (s: Uint8Array) => void) => IDisposable; - }) { - const result = opts.subject.result; - const target = opts.getTarget(result); - if (!target) { - return this.clear(); - } - - const terminal = await this.makeTerminal(); - let didWriteData = false; - - const pendingWrites = new MutableObservableValue(0); - if (result instanceof LiveTestResult) { - for (const chunk of opts.doInitialWrite(target, result)) { - didWriteData ||= chunk.byteLength > 0; - pendingWrites.value++; - terminal.xterm.write(chunk.buffer, () => pendingWrites.value--); - } - } else { - didWriteData = true; - this.writeNotice(terminal, localize('runNoOutputForPast', 'Test output is only available for new test runs.')); - } - - this.attachTerminalToDom(terminal); - this.outputDataListener.clear(); - - if (result instanceof LiveTestResult && !result.completedAt) { - const l1 = result.onComplete(() => { - if (!didWriteData) { - this.writeNotice(terminal, opts.noOutputMessage); - } - }); - const l2 = opts.doListenForMoreData(target, result, data => { - terminal.xterm.write(data); - didWriteData ||= data.byteLength > 0; - }); - - this.outputDataListener.value = combinedDisposable(l1, l2); - } - - if (!this.outputDataListener.value && !didWriteData) { - this.writeNotice(terminal, opts.noOutputMessage); - } - - // Ensure pending writes finish, otherwise the selection in `updateForTestSubject` - // can happen before the markers are processed. - if (pendingWrites.value > 0) { - await new Promise(resolve => { - const l = pendingWrites.onDidChange(() => { - if (pendingWrites.value === 0) { - l.dispose(); - resolve(); - } - }); - }); - } - - return terminal; - } - - private updateCwd(testUri?: URI) { - const wf = (testUri && this.workspaceContext.getWorkspaceFolder(testUri)) - || this.workspaceContext.getWorkspace().folders[0]; - if (wf) { - this.terminalCwd.value = wf.uri.fsPath; - } - } - - private writeNotice(terminal: IDetachedTerminalInstance, str: string) { - terminal.xterm.write(formatMessageForTerminal(str)); - } - - private attachTerminalToDom(terminal: IDetachedTerminalInstance) { - terminal.xterm.write('\x1b[?25l'); // hide cursor - dom.scheduleAtNextAnimationFrame(dom.getWindow(this.container), () => this.layoutTerminal(terminal)); - terminal.attachToElement(this.container, { enableGpu: false }); - } - - private clear() { - this.outputDataListener.clear(); - this.xtermLayoutDelayer.cancel(); - this.terminal.clear(); - } - - public layout(dimensions: dom.IDimension) { - this.dimensions = dimensions; - if (this.terminal.value) { - this.layoutTerminal(this.terminal.value, dimensions.width, dimensions.height); - } - } - - private layoutTerminal( - { xterm }: IDetachedTerminalInstance, - width = this.dimensions?.width ?? this.container.clientWidth, - height = this.dimensions?.height ?? this.container.clientHeight - ) { - width -= 10 + 20; // scrollbar width + margin - this.xtermLayoutDelayer.trigger(() => { - const scaled = getXtermScaledDimensions(dom.getWindow(this.container), xterm.getFont(), width, height); - if (scaled) { - xterm.resize(scaled.cols, scaled.rows); - } - }); - } -} - const hintMessagePeekHeight = (msg: ITestMessage) => { - const msgHeight = isDiffable(msg) + const msgHeight = ITestMessage.isDiffable(msg) ? Math.max(hintPeekStrHeight(msg.actual), hintPeekStrHeight(msg.expected)) : hintPeekStrHeight(typeof msg.message === 'string' ? msg.message : msg.message.value); @@ -1742,28 +1148,9 @@ const firstLine = (str: string) => { return index === -1 ? str : str.slice(0, index); }; -const isMultiline = (str: string | undefined) => !!str && str.includes('\n'); const hintPeekStrHeight = (str: string) => Math.min(count(str, '\n'), 24); -class SimpleDiffEditorModel extends EditorModel { - public readonly original = this._original.object.textEditorModel; - public readonly modified = this._modified.object.textEditorModel; - - constructor( - private readonly _original: IReference, - private readonly _modified: IReference, - ) { - super(); - } - - public override dispose() { - super.dispose(); - this._original.dispose(); - this._modified.dispose(); - } -} - function getOuterEditorFromDiffEditor(codeEditorService: ICodeEditorService): ICodeEditor | null { const diffEditors = codeEditorService.listDiffEditors(); @@ -1797,771 +1184,6 @@ export class CloseTestPeek extends EditorAction2 { } } -interface ITreeElement { - type: string; - context: unknown; - id: string; - label: string; - onDidChange: Event; - labelWithIcons?: readonly (HTMLSpanElement | string)[]; - icon?: ThemeIcon; - description?: string; - ariaLabel?: string; -} - -class TestResultElement implements ITreeElement { - public readonly changeEmitter = new Emitter(); - public readonly onDidChange = this.changeEmitter.event; - public readonly type = 'result'; - public readonly context = this.value.id; - public readonly id = this.value.id; - public readonly label = this.value.name; - - public get icon() { - return icons.testingStatesToIcons.get( - this.value.completedAt === undefined - ? TestResultState.Running - : maxCountPriority(this.value.counts) - ); - } - - constructor(public readonly value: ITestResult) { } -} - -const openCoverageLabel = localize('openTestCoverage', 'View Test Coverage'); -const closeCoverageLabel = localize('closeTestCoverage', 'Close Test Coverage'); - -class CoverageElement implements ITreeElement { - public readonly type = 'coverage'; - public readonly context: undefined; - public readonly id = `coverage-${this.results.id}/${this.task.id}`; - public readonly onDidChange: Event; - - public get label() { - return this.isOpen ? closeCoverageLabel : openCoverageLabel; - } - - public get icon() { - return this.isOpen ? widgetClose : icons.testingCoverageReport; - } - - public get isOpen() { - return this.coverageService.selected.get()?.fromTaskId === this.task.id; - } - - constructor( - private readonly results: ITestResult, - public readonly task: ITestRunTaskResults, - private readonly coverageService: ITestCoverageService, - ) { - this.onDidChange = Event.fromObservableLight(coverageService.selected); - } - -} - -class TestCaseElement implements ITreeElement { - public readonly type = 'test'; - public readonly context: ITestItemContext = { - $mid: MarshalledId.TestItemContext, - tests: [InternalTestItem.serialize(this.test)], - }; - public readonly id = `${this.results.id}/${this.test.item.extId}`; - public readonly description?: string; - - public get onDidChange() { - if (!(this.results instanceof LiveTestResult)) { - return Event.None; - } - - return Event.filter(this.results.onChange, e => e.item.item.extId === this.test.item.extId); - } - - public get state() { - return this.test.tasks[this.taskIndex].state; - } - - public get label() { - return this.test.item.label; - } - - public get labelWithIcons() { - return renderLabelWithIcons(this.label); - } - - public get icon() { - return icons.testingStatesToIcons.get(this.state); - } - - public get outputSubject() { - return new TestOutputSubject(this.results, this.taskIndex, this.test); - } - - - constructor( - public readonly results: ITestResult, - public readonly test: TestResultItem, - public readonly taskIndex: number, - ) { } -} - -class TaskElement implements ITreeElement { - public readonly changeEmitter = new Emitter(); - public readonly onDidChange = this.changeEmitter.event; - public readonly type = 'task'; - public readonly context: string; - public readonly id: string; - public readonly label: string; - public readonly itemsCache = new CreationCache(); - - public get icon() { - return this.results.tasks[this.index].running ? icons.testingStatesToIcons.get(TestResultState.Running) : undefined; - } - - constructor(public readonly results: ITestResult, public readonly task: ITestRunTaskResults, public readonly index: number) { - this.id = `${results.id}/${index}`; - this.task = results.tasks[index]; - this.context = String(index); - this.label = this.task.name ?? localize('testUnnamedTask', 'Unnamed Task'); - } -} - -class TestMessageElement implements ITreeElement { - public readonly type = 'message'; - public readonly id: string; - public readonly label: string; - public readonly uri: URI; - public readonly location?: IRichLocation; - public readonly description?: string; - public readonly contextValue?: string; - public readonly message: ITestMessage; - - public get onDidChange() { - if (!(this.result instanceof LiveTestResult)) { - return Event.None; - } - - // rerender when the test case changes so it gets retired events - return Event.filter(this.result.onChange, e => e.item.item.extId === this.test.item.extId); - } - - public get context(): ITestMessageMenuArgs { - return getMessageArgs(this.test, this.message); - } - - public get outputSubject() { - return new TestOutputSubject(this.result, this.taskIndex, this.test); - } - - constructor( - public readonly result: ITestResult, - public readonly test: TestResultItem, - public readonly taskIndex: number, - public readonly messageIndex: number, - ) { - const m = this.message = test.tasks[taskIndex].messages[messageIndex]; - - this.location = m.location; - this.contextValue = m.type === TestMessageType.Error ? m.contextValue : undefined; - this.uri = buildTestUri({ - type: TestUriType.ResultMessage, - messageIndex, - resultId: result.id, - taskIndex, - testExtId: test.item.extId - }); - - this.id = this.uri.toString(); - - const asPlaintext = renderTestMessageAsText(m.message); - const lines = count(asPlaintext.trimEnd(), '\n'); - this.label = firstLine(asPlaintext); - if (lines > 0) { - this.description = lines > 1 - ? localize('messageMoreLinesN', '+ {0} more lines', lines) - : localize('messageMoreLines1', '+ 1 more line'); - } - } -} - -type TreeElement = TestResultElement | TestCaseElement | TestMessageElement | TaskElement | CoverageElement; - -class OutputPeekTree extends Disposable { - private disposed = false; - private readonly tree: WorkbenchCompressibleObjectTree; - private readonly treeActions: TreeActionsProvider; - private readonly requestReveal = this._register(new Emitter()); - - public readonly onDidRequestReview = this.requestReveal.event; - - constructor( - container: HTMLElement, - onDidReveal: Event<{ subject: InspectSubject; preserveFocus: boolean }>, - options: { showRevealLocationOnMessages: boolean; locationForProgress: string }, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @ITestResultService results: ITestResultService, - @IInstantiationService instantiationService: IInstantiationService, - @ITestExplorerFilterState explorerFilter: ITestExplorerFilterState, - @ITestCoverageService coverageService: ITestCoverageService, - @IProgressService progressService: IProgressService, - ) { - super(); - - this.treeActions = instantiationService.createInstance(TreeActionsProvider, options.showRevealLocationOnMessages, this.requestReveal,); - const diffIdentityProvider: IIdentityProvider = { - getId(e: TreeElement) { - return e.id; - } - }; - - this.tree = this._register(instantiationService.createInstance( - WorkbenchCompressibleObjectTree, - 'Test Output Peek', - container, - { - getHeight: () => 22, - getTemplateId: () => TestRunElementRenderer.ID, - }, - [instantiationService.createInstance(TestRunElementRenderer, this.treeActions)], - { - compressionEnabled: true, - hideTwistiesOfChildlessElements: true, - identityProvider: diffIdentityProvider, - sorter: { - compare(a, b) { - if (a instanceof TestCaseElement && b instanceof TestCaseElement) { - return cmpPriority(a.state, b.state); - } - - return 0; - }, - }, - accessibilityProvider: { - getAriaLabel(element: ITreeElement) { - return element.ariaLabel || element.label; - }, - getWidgetAriaLabel() { - return localize('testingPeekLabel', 'Test Result Messages'); - } - } - }, - )) as WorkbenchCompressibleObjectTree; - - const cc = new CreationCache(); - const getTaskChildren = (taskElem: TaskElement): Iterable> => { - const { results, index, itemsCache, task } = taskElem; - const tests = Iterable.filter(results.tests, test => test.tasks[index].state >= TestResultState.Running || test.tasks[index].messages.length > 0); - let result: Iterable> = Iterable.map(tests, test => ({ - element: itemsCache.getOrCreate(test, () => new TestCaseElement(results, test, index)), - incompressible: true, - children: getTestChildren(results, test, index), - })); - - if (task.coverage.get()) { - result = Iterable.concat( - Iterable.single>({ - element: new CoverageElement(results, task, coverageService), - collapsible: true, - incompressible: true, - }), - result, - ); - } - - return result; - }; - - const getTestChildren = (result: ITestResult, test: TestResultItem, taskIndex: number): Iterable> => { - return test.tasks[taskIndex].messages - .map((m, messageIndex) => - m.type === TestMessageType.Error - ? { element: cc.getOrCreate(m, () => new TestMessageElement(result, test, taskIndex, messageIndex)), incompressible: false } - : undefined - ) - .filter(isDefined); - }; - - const getResultChildren = (result: ITestResult): Iterable> => { - return result.tasks.map((task, taskIndex) => { - const taskElem = cc.getOrCreate(task, () => new TaskElement(result, task, taskIndex)); - return ({ - element: taskElem, - incompressible: false, - collapsible: true, - children: getTaskChildren(taskElem), - }); - }); - }; - - const getRootChildren = () => results.results.map(result => { - const element = cc.getOrCreate(result, () => new TestResultElement(result)); - return { - element, - incompressible: true, - collapsible: true, - collapsed: this.tree.hasElement(element) ? this.tree.isCollapsed(element) : true, - children: getResultChildren(result) - }; - }); - - // Queued result updates to prevent spamming CPU when lots of tests are - // completing and messaging quickly (#142514) - const taskChildrenToUpdate = new Set(); - const taskChildrenUpdate = this._register(new RunOnceScheduler(() => { - for (const taskNode of taskChildrenToUpdate) { - if (this.tree.hasElement(taskNode)) { - this.tree.setChildren(taskNode, getTaskChildren(taskNode), { diffIdentityProvider }); - } - } - taskChildrenToUpdate.clear(); - }, 300)); - - const queueTaskChildrenUpdate = (taskNode: TaskElement) => { - taskChildrenToUpdate.add(taskNode); - if (!taskChildrenUpdate.isScheduled()) { - taskChildrenUpdate.schedule(); - } - }; - - const attachToResults = (result: LiveTestResult) => { - const resultNode = cc.get(result)! as TestResultElement; - const disposable = new DisposableStore(); - disposable.add(result.onNewTask(i => { - if (result.tasks.length === 1) { - this.requestReveal.fire(new TaskSubject(result, 0)); // reveal the first task in new runs - } - - if (this.tree.hasElement(resultNode)) { - this.tree.setChildren(resultNode, getResultChildren(result), { diffIdentityProvider }); - } - - // note: tasks are bounded and their lifetime is equivalent to that of - // the test result, so this doesn't leak indefinitely. - const task = result.tasks[i]; - disposable.add(autorun(reader => { - task.coverage.read(reader); // add it to the autorun - queueTaskChildrenUpdate(cc.get(task) as TaskElement); - })); - })); - disposable.add(result.onEndTask(index => { - (cc.get(result.tasks[index]) as TaskElement | undefined)?.changeEmitter.fire(); - })); - - disposable.add(result.onChange(e => { - // try updating the item in each of its tasks - for (const [index, task] of result.tasks.entries()) { - const taskNode = cc.get(task) as TaskElement; - if (!this.tree.hasElement(taskNode)) { - continue; - } - - const itemNode = taskNode.itemsCache.get(e.item); - if (itemNode && this.tree.hasElement(itemNode)) { - if (e.reason === TestResultItemChangeReason.NewMessage && e.message.type === TestMessageType.Error) { - this.tree.setChildren(itemNode, getTestChildren(result, e.item, index), { diffIdentityProvider }); - } - return; - } - - queueTaskChildrenUpdate(taskNode); - } - })); - - disposable.add(result.onComplete(() => { - resultNode.changeEmitter.fire(); - disposable.dispose(); - })); - - return resultNode; - }; - - this._register(results.onResultsChanged(e => { - // little hack here: a result change can cause the peek to be disposed, - // but this listener will still be queued. Doing stuff with the tree - // will cause errors. - if (this.disposed) { - return; - } - - if ('completed' in e) { - (cc.get(e.completed) as TestResultElement | undefined)?.changeEmitter.fire(); - return; - } - - this.tree.setChildren(null, getRootChildren(), { diffIdentityProvider }); - - // done after setChildren intentionally so that the ResultElement exists in the cache. - if ('started' in e) { - for (const child of this.tree.getNode(null).children) { - this.tree.collapse(child.element, false); - } - - this.tree.expand(attachToResults(e.started), true); - } - })); - - const revealItem = (element: TreeElement, preserveFocus: boolean) => { - this.tree.setFocus([element]); - this.tree.setSelection([element]); - if (!preserveFocus) { - this.tree.domFocus(); - } - }; - - this._register(onDidReveal(async ({ subject, preserveFocus = false }) => { - if (subject instanceof TaskSubject) { - const resultItem = this.tree.getNode(null).children.find(c => { - if (c.element instanceof TaskElement) { - return c.element.results.id === subject.result.id && c.element.index === subject.taskIndex; - } - if (c.element instanceof TestResultElement) { - return c.element.id === subject.result.id; - } - return false; - }); - - if (resultItem) { - revealItem(resultItem.element!, preserveFocus); - } - return; - } - - const revealElement = subject instanceof TestOutputSubject - ? cc.get(subject.task)?.itemsCache.get(subject.test) - : cc.get(subject.message); - if (!revealElement || !this.tree.hasElement(revealElement)) { - return; - } - - const parents: TreeElement[] = []; - for (let parent = this.tree.getParentElement(revealElement); parent; parent = this.tree.getParentElement(parent)) { - parents.unshift(parent); - } - - for (const parent of parents) { - this.tree.expand(parent); - } - - if (this.tree.getRelativeTop(revealElement) === null) { - this.tree.reveal(revealElement, 0.5); - } - - revealItem(revealElement, preserveFocus); - })); - - this._register(this.tree.onDidOpen(async e => { - if (e.element instanceof TestMessageElement) { - this.requestReveal.fire(new MessageSubject(e.element.result, e.element.test, e.element.taskIndex, e.element.messageIndex)); - } else if (e.element instanceof TestCaseElement) { - const t = e.element; - const message = mapFindTestMessage(e.element.test, (_t, _m, mesasgeIndex, taskIndex) => - new MessageSubject(t.results, t.test, taskIndex, mesasgeIndex)); - this.requestReveal.fire(message || new TestOutputSubject(t.results, 0, t.test)); - } else if (e.element instanceof CoverageElement) { - const task = e.element.task; - if (e.element.isOpen) { - return coverageService.closeCoverage(); - } - progressService.withProgress( - { location: options.locationForProgress }, - () => coverageService.openCoverage(task, true) - ); - } - })); - - this._register(this.tree.onDidChangeSelection(evt => { - for (const element of evt.elements) { - if (element && 'test' in element) { - explorerFilter.reveal.value = element.test.item.extId; - break; - } - } - })); - - - this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); - - this.tree.setChildren(null, getRootChildren()); - for (const result of results.results) { - if (!result.completedAt && result instanceof LiveTestResult) { - attachToResults(result); - } - } - } - - public layout(height: number, width: number) { - this.tree.layout(height, width); - } - - private onContextMenu(evt: ITreeContextMenuEvent) { - if (!evt.element) { - return; - } - - const actions = this.treeActions.provideActionBar(evt.element); - this.contextMenuService.showContextMenu({ - getAnchor: () => evt.anchor, - getActions: () => actions.secondary.length - ? [...actions.primary, new Separator(), ...actions.secondary] - : actions.primary, - getActionsContext: () => evt.element?.context - }); - } - - public override dispose() { - super.dispose(); - this.disposed = true; - } -} - -interface TemplateData { - label: HTMLElement; - icon: HTMLElement; - actionBar: ActionBar; - elementDisposable: DisposableStore; - templateDisposable: DisposableStore; -} - -class TestRunElementRenderer implements ICompressibleTreeRenderer { - public static readonly ID = 'testRunElementRenderer'; - public readonly templateId = TestRunElementRenderer.ID; - - constructor( - private readonly treeActions: TreeActionsProvider, - @IInstantiationService private readonly instantiationService: IInstantiationService, - ) { } - - /** @inheritdoc */ - public renderCompressedElements(node: ITreeNode, FuzzyScore>, _index: number, templateData: TemplateData): void { - const chain = node.element.elements; - const lastElement = chain[chain.length - 1]; - if ((lastElement instanceof TaskElement || lastElement instanceof TestMessageElement) && chain.length >= 2) { - this.doRender(chain[chain.length - 2], templateData, lastElement); - } else { - this.doRender(lastElement, templateData); - } - } - - /** @inheritdoc */ - public renderTemplate(container: HTMLElement): TemplateData { - const templateDisposable = new DisposableStore(); - const wrapper = dom.append(container, dom.$('.test-peek-item')); - const icon = dom.append(wrapper, dom.$('.state')); - const label = dom.append(wrapper, dom.$('.name')); - - const actionBar = new ActionBar(wrapper, { - actionViewItemProvider: (action, options) => - action instanceof MenuItemAction - ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) - : undefined - }); - - const elementDisposable = new DisposableStore(); - templateDisposable.add(elementDisposable); - templateDisposable.add(actionBar); - - return { - icon, - label, - actionBar, - elementDisposable, - templateDisposable, - }; - } - - /** @inheritdoc */ - public renderElement(element: ITreeNode, _index: number, templateData: TemplateData): void { - this.doRender(element.element, templateData); - } - - /** @inheritdoc */ - public disposeTemplate(templateData: TemplateData): void { - templateData.templateDisposable.dispose(); - } - - /** Called to render a new element */ - private doRender(element: ITreeElement, templateData: TemplateData, subjectElement?: ITreeElement) { - templateData.elementDisposable.clear(); - templateData.elementDisposable.add( - element.onDidChange(() => this.doRender(element, templateData, subjectElement)), - ); - this.doRenderInner(element, templateData, subjectElement); - } - - /** Called, and may be re-called, to render or re-render an element */ - private doRenderInner(element: ITreeElement, templateData: TemplateData, subjectElement: ITreeElement | undefined) { - let { label, labelWithIcons, description } = element; - if (subjectElement instanceof TestMessageElement) { - description = subjectElement.label; - } - - const descriptionElement = description ? dom.$('span.test-label-description', {}, description) : ''; - if (labelWithIcons) { - dom.reset(templateData.label, ...labelWithIcons, descriptionElement); - } else { - dom.reset(templateData.label, label, descriptionElement); - } - - const icon = element.icon; - templateData.icon.className = `computed-state ${icon ? ThemeIcon.asClassName(icon) : ''}`; - - const actions = this.treeActions.provideActionBar(element); - templateData.actionBar.clear(); - templateData.actionBar.context = element.context; - templateData.actionBar.push(actions.primary, { icon: true, label: false }); - } -} - -class TreeActionsProvider { - constructor( - private readonly showRevealLocationOnMessages: boolean, - private readonly requestReveal: Emitter, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService, - @ICommandService private readonly commandService: ICommandService, - @ITestProfileService private readonly testProfileService: ITestProfileService, - @IEditorService private readonly editorService: IEditorService, - ) { } - - public provideActionBar(element: ITreeElement) { - const test = element instanceof TestCaseElement ? element.test : undefined; - const capabilities = test ? this.testProfileService.capabilitiesForTest(test) : 0; - - const contextKeys: [string, unknown][] = [ - ['peek', Testing.OutputPeekContributionId], - [TestingContextKeys.peekItemType.key, element.type], - ]; - - let id = MenuId.TestPeekElement; - const primary: IAction[] = []; - const secondary: IAction[] = []; - - if (element instanceof TaskElement) { - primary.push(new Action( - 'testing.outputPeek.showResultOutput', - localize('testing.showResultOutput', "Show Result Output"), - ThemeIcon.asClassName(Codicon.terminal), - undefined, - () => this.requestReveal.fire(new TaskSubject(element.results, element.index)), - )); - } - - if (element instanceof TestResultElement) { - // only show if there are no collapsed test nodes that have more specific choices - if (element.value.tasks.length === 1) { - primary.push(new Action( - 'testing.outputPeek.showResultOutput', - localize('testing.showResultOutput', "Show Result Output"), - ThemeIcon.asClassName(Codicon.terminal), - undefined, - () => this.requestReveal.fire(new TaskSubject(element.value, 0)), - )); - } - - primary.push(new Action( - 'testing.outputPeek.reRunLastRun', - localize('testing.reRunLastRun', "Rerun Test Run"), - ThemeIcon.asClassName(icons.testingRunIcon), - undefined, - () => this.commandService.executeCommand('testing.reRunLastRun', element.value.id), - )); - - if (capabilities & TestRunProfileBitset.Debug) { - primary.push(new Action( - 'testing.outputPeek.debugLastRun', - localize('testing.debugLastRun', "Debug Test Run"), - ThemeIcon.asClassName(icons.testingDebugIcon), - undefined, - () => this.commandService.executeCommand('testing.debugLastRun', element.value.id), - )); - } - } - - if (element instanceof TestCaseElement || element instanceof TestMessageElement) { - contextKeys.push( - [TestingContextKeys.testResultOutdated.key, element.test.retired], - [TestingContextKeys.testResultState.key, testResultStateToContextValues[element.test.ownComputedState]], - ...getTestItemContextOverlay(element.test, capabilities), - ); - - const extId = element.test.item.extId; - if (element.test.tasks[element.taskIndex].messages.some(m => m.type === TestMessageType.Output)) { - primary.push(new Action( - 'testing.outputPeek.showResultOutput', - localize('testing.showResultOutput', "Show Result Output"), - ThemeIcon.asClassName(Codicon.terminal), - undefined, - () => this.requestReveal.fire(element.outputSubject), - )); - } - - secondary.push(new Action( - 'testing.outputPeek.revealInExplorer', - localize('testing.revealInExplorer', "Reveal in Test Explorer"), - ThemeIcon.asClassName(Codicon.listTree), - undefined, - () => this.commandService.executeCommand('_revealTestInExplorer', extId), - )); - - if (capabilities & TestRunProfileBitset.Run) { - primary.push(new Action( - 'testing.outputPeek.runTest', - localize('run test', 'Run Test'), - ThemeIcon.asClassName(icons.testingRunIcon), - undefined, - () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Run, extId), - )); - } - - if (capabilities & TestRunProfileBitset.Debug) { - primary.push(new Action( - 'testing.outputPeek.debugTest', - localize('debug test', 'Debug Test'), - ThemeIcon.asClassName(icons.testingDebugIcon), - undefined, - () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Debug, extId), - )); - } - - } - - if (element instanceof TestMessageElement) { - primary.push(new Action( - 'testing.outputPeek.goToFile', - localize('testing.goToFile', "Go to Source"), - ThemeIcon.asClassName(Codicon.goToFile), - undefined, - () => this.commandService.executeCommand('vscode.revealTest', element.test.item.extId), - )); - } - - if (element instanceof TestMessageElement) { - id = MenuId.TestMessageContext; - contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]); - if (this.showRevealLocationOnMessages && element.location) { - primary.push(new Action( - 'testing.outputPeek.goToError', - localize('testing.goToError', "Go to Source"), - ThemeIcon.asClassName(Codicon.goToFile), - undefined, - () => this.editorService.openEditor({ - resource: element.location!.uri, - options: { - selection: element.location!.range, - preserveFocus: true, - } - }), - )); - } - } - - - const contextOverlay = this.contextKeyService.createOverlay(contextKeys); - const result = { primary, secondary }; - const menu = this.menuService.getMenuActions(id, contextOverlay, { arg: element.context }); - createAndFillInActionBarActions(menu, result, 'inline'); - return result; - } -} const navWhen = ContextKeyExpr.and( EditorContextKeys.focus, @@ -2717,22 +1339,3 @@ export class ToggleTestingPeekHistory extends Action2 { opener.historyVisible.value = !opener.historyVisible.value; } } - -class CreationCache { - private readonly v = new WeakMap(); - - public get(key: object): T2 | undefined { - return this.v.get(key) as T2 | undefined; - } - - public getOrCreate(ref: object, factory: () => T2): T2 { - const existing = this.v.get(ref); - if (existing) { - return existing as T2; - } - - const fresh = factory(); - this.v.set(ref, fresh); - return fresh; - } -} diff --git a/src/vs/workbench/contrib/testing/common/testTypes.ts b/src/vs/workbench/contrib/testing/common/testTypes.ts index 5a90948fc68..f551217600e 100644 --- a/src/vs/workbench/contrib/testing/common/testTypes.ts +++ b/src/vs/workbench/contrib/testing/common/testTypes.ts @@ -169,6 +169,32 @@ export const enum TestMessageType { Output } +export interface ITestMessageStackTrace { + label: string; + uri: URI | undefined; + position: Position | undefined; +} + +export namespace ITestMessageStackTrace { + export interface Serialized { + label: string; + uri: UriComponents | undefined; + position: IPosition | undefined; + } + + export const serialize = (stack: Readonly): Serialized => ({ + label: stack.label, + uri: stack.uri?.toJSON(), + position: stack.position?.toJSON(), + }); + + export const deserialize = (uriIdentity: ITestUriCanonicalizer, stack: Serialized): ITestMessageStackTrace => ({ + label: stack.label, + uri: stack.uri ? uriIdentity.asCanonicalUri(URI.revive(stack.uri)) : undefined, + position: stack.position ? Position.lift(stack.position) : undefined, + }); +} + export interface ITestErrorMessage { message: string | IMarkdownString; type: TestMessageType.Error; @@ -176,6 +202,7 @@ export interface ITestErrorMessage { actual: string | undefined; contextValue: string | undefined; location: IRichLocation | undefined; + stackTrace: undefined | ITestMessageStackTrace[]; } export namespace ITestErrorMessage { @@ -186,6 +213,7 @@ export namespace ITestErrorMessage { actual: string | undefined; contextValue: string | undefined; location: IRichLocation.Serialize | undefined; + stackTrace: undefined | ITestMessageStackTrace.Serialized[]; } export const serialize = (message: Readonly): Serialized => ({ @@ -195,6 +223,7 @@ export namespace ITestErrorMessage { actual: message.actual, contextValue: message.contextValue, location: message.location && IRichLocation.serialize(message.location), + stackTrace: message.stackTrace?.map(ITestMessageStackTrace.serialize), }); export const deserialize = (uriIdentity: ITestUriCanonicalizer, message: Serialized): ITestErrorMessage => ({ @@ -204,6 +233,7 @@ export namespace ITestErrorMessage { actual: message.actual, contextValue: message.contextValue, location: message.location && IRichLocation.deserialize(uriIdentity, message.location), + stackTrace: message.stackTrace && message.stackTrace.map(s => ITestMessageStackTrace.deserialize(uriIdentity, s)), }); } @@ -258,6 +288,9 @@ export namespace ITestMessage { export const deserialize = (uriIdentity: ITestUriCanonicalizer, message: Serialized): ITestMessage => message.type === TestMessageType.Error ? ITestErrorMessage.deserialize(uriIdentity, message) : ITestOutputMessage.deserialize(uriIdentity, message); + + export const isDiffable = (message: ITestMessage): message is ITestErrorMessage & { actual: string; expected: string } => + message.type === TestMessageType.Error && message.actual !== undefined && message.expected !== undefined; } export interface ITestTaskState { diff --git a/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts b/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts new file mode 100644 index 00000000000..b3f09b92835 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + export class TestMessage2 extends TestMessage { + /** + * The stack trace associated with the message or failure. + */ + stackTrace?: TestMessageStackFrame[]; + } + + export class TestMessageStackFrame { + /** + * The location of this stack frame. This should be provided as a URI if the + * location of the call frame can be accessed by the editor. + */ + file?: Uri; + + /** + * Position of the stack frame within the file. + */ + position?: Position; + + /** + * The name of the stack frame, typically a method or function name. + */ + label: string; + + /** + * @param label The name of the stack frame + * @param file The file URI of the stack frame + * @param position The position of the stack frame within the file + */ + constructor(label: string, file?: Uri, position?: Position); + } +} From fecf501d5569125c1b08821838c584fc4c0d0ad4 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 8 Jul 2024 15:52:05 -0700 Subject: [PATCH 010/798] fix tests, more split --- .../api/test/browser/extHostTesting.test.ts | 4 +- .../testResultsView/testResultsViewContent.ts | 305 ++++++++++++++++++ .../testing/browser/testingOutputPeek.ts | 293 +---------------- 3 files changed, 314 insertions(+), 288 deletions(-) create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index c4515796d05..4a49c8031a2 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -829,7 +829,8 @@ suite('ExtHost Testing', () => { expected: undefined, contextValue: undefined, actual: undefined, - location: convert.location.from(message1.location) + location: convert.location.from(message1.location), + stackTrace: undefined, }] ]); @@ -846,6 +847,7 @@ suite('ExtHost Testing', () => { expected: undefined, actual: undefined, location: convert.location.from({ uri: test2.uri!, range: test2.range }), + stackTrace: undefined, }] ]); diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts new file mode 100644 index 00000000000..2bdc631d95e --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts @@ -0,0 +1,305 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; +import { Limiter } from 'vs/base/common/async'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./testingOutputPeek'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { localize } from 'vs/nls'; +import { FloatingClickMenu } from 'vs/platform/actions/browser/floatingMenu'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput'; +import { InspectSubject, MessageSubject, equalsSubject } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { OutputPeekTree } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree'; +import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; +import { IObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; +import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestFollowup, ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; + + +export class TestResultsViewContent extends Disposable { + private static lastSplitWidth?: number; + + private readonly didReveal = this._register(new Emitter<{ subject: InspectSubject; preserveFocus: boolean }>()); + private readonly currentSubjectStore = this._register(new DisposableStore()); + private followupWidget!: FollowupActionWidget; + private messageContextKeyService!: IContextKeyService; + private contextKeyTestMessage!: IContextKey; + private contextKeyResultOutdated!: IContextKey; + + private dimension?: dom.Dimension; + private splitView!: SplitView; + private messageContainer!: HTMLElement; + private contentProviders!: IPeekOutputRenderer[]; + private contentProvidersUpdateLimiter = this._register(new Limiter(1)); + + public current?: InspectSubject; + + /** Fired when a tree item is selected. Populated only on .fillBody() */ + public onDidRequestReveal!: Event; + + constructor( + private readonly editor: ICodeEditor | undefined, + private readonly options: { + historyVisible: IObservableValue; + showRevealLocationOnMessages: boolean; + locationForProgress: string; + }, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextModelService protected readonly modelService: ITextModelService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { + super(); + } + + public fillBody(containerElement: HTMLElement): void { + const initialSpitWidth = TestResultsViewContent.lastSplitWidth; + this.splitView = new SplitView(containerElement, { orientation: Orientation.HORIZONTAL }); + + const { historyVisible, showRevealLocationOnMessages } = this.options; + const isInPeekView = this.editor !== undefined; + const messageContainer = this.messageContainer = dom.append(containerElement, dom.$('.test-output-peek-message-container')); + this.followupWidget = this._register(this.instantiationService.createInstance(FollowupActionWidget, messageContainer, this.editor)); + this.contentProviders = [ + this._register(this.instantiationService.createInstance(DiffContentProvider, this.editor, messageContainer)), + this._register(this.instantiationService.createInstance(MarkdownTestMessagePeek, messageContainer)), + this._register(this.instantiationService.createInstance(TerminalMessagePeek, messageContainer, isInPeekView)), + this._register(this.instantiationService.createInstance(PlainTextMessagePeek, this.editor, messageContainer)), + ]; + + this.messageContextKeyService = this._register(this.contextKeyService.createScoped(containerElement)); + this.contextKeyTestMessage = TestingContextKeys.testMessageContext.bindTo(this.messageContextKeyService); + this.contextKeyResultOutdated = TestingContextKeys.testResultOutdated.bindTo(this.messageContextKeyService); + + const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree')); + const tree = this._register(this.instantiationService.createInstance( + OutputPeekTree, + treeContainer, + this.didReveal.event, + { showRevealLocationOnMessages, locationForProgress: this.options.locationForProgress }, + )); + + this.onDidRequestReveal = tree.onDidRequestReview; + + this.splitView.addView({ + onDidChange: Event.None, + element: messageContainer, + minimumSize: 200, + maximumSize: Number.MAX_VALUE, + layout: width => { + TestResultsViewContent.lastSplitWidth = width; + if (this.dimension) { + for (const provider of this.contentProviders) { + provider.layout({ height: this.dimension.height, width }); + } + } + }, + }, Sizing.Distribute); + + this.splitView.addView({ + onDidChange: Event.None, + element: treeContainer, + minimumSize: 100, + maximumSize: Number.MAX_VALUE, + layout: width => { + if (this.dimension) { + tree.layout(this.dimension.height, width); + } + }, + }, Sizing.Distribute); + + const historyViewIndex = 1; + this.splitView.setViewVisible(historyViewIndex, historyVisible.value); + this._register(historyVisible.onDidChange(visible => { + this.splitView.setViewVisible(historyViewIndex, visible); + })); + + if (initialSpitWidth) { + queueMicrotask(() => this.splitView.resizeView(0, initialSpitWidth)); + } + } + + /** + * Shows a message in-place without showing or changing the peek location. + * This is mostly used if peeking a message without a location. + */ + public reveal(opts: { subject: InspectSubject; preserveFocus: boolean }) { + this.didReveal.fire(opts); + + if (this.current && equalsSubject(this.current, opts.subject)) { + return Promise.resolve(); + } + + this.current = opts.subject; + return this.contentProvidersUpdateLimiter.queue(async () => { + await Promise.all(this.contentProviders.map(p => p.update(opts.subject))); + this.followupWidget.show(opts.subject); + this.currentSubjectStore.clear(); + this.populateFloatingClick(opts.subject); + }); + } + + private populateFloatingClick(subject: InspectSubject) { + if (!(subject instanceof MessageSubject)) { + return; + } + + this.currentSubjectStore.add(toDisposable(() => { + this.contextKeyResultOutdated.reset(); + this.contextKeyTestMessage.reset(); + })); + + this.contextKeyTestMessage.set(subject.contextValue || ''); + if (subject.result instanceof LiveTestResult) { + this.contextKeyResultOutdated.set(subject.result.getStateById(subject.test.extId)?.retired ?? false); + this.currentSubjectStore.add(subject.result.onChange(ev => { + if (ev.item.item.extId === subject.test.extId) { + this.contextKeyResultOutdated.set(ev.item.retired ?? false); + } + })); + } else { + this.contextKeyResultOutdated.set(true); + } + + const instaService = this.currentSubjectStore.add(this.instantiationService + .createChild(new ServiceCollection([IContextKeyService, this.messageContextKeyService]))); + + this.currentSubjectStore.add(instaService.createInstance(FloatingClickMenu, { + container: this.messageContainer, + menuId: MenuId.TestMessageContent, + getActionArg: () => (subject as MessageSubject).context, + })); + } + + public onLayoutBody(height: number, width: number) { + this.dimension = new dom.Dimension(width, height); + this.splitView.layout(width); + } + + public onWidth(width: number) { + this.splitView.layout(width); + } +} + +const FOLLOWUP_ANIMATION_MIN_TIME = 500; + +class FollowupActionWidget extends Disposable { + private readonly el = dom.h('div.testing-followup-action', []); + private readonly visibleStore = this._register(new DisposableStore()); + + constructor( + private readonly container: HTMLElement, + private readonly editor: ICodeEditor | undefined, + @ITestService private readonly testService: ITestService, + @IQuickInputService private readonly quickInput: IQuickInputService, + ) { + super(); + } + + public show(subject: InspectSubject) { + this.visibleStore.clear(); + if (subject instanceof MessageSubject) { + this.showMessage(subject); + } + } + + private async showMessage(subject: MessageSubject) { + const cts = this.visibleStore.add(new CancellationTokenSource()); + const start = Date.now(); + + // Wait for completion otherwise results will not be available to the ext host: + if (subject.result instanceof LiveTestResult && !subject.result.completedAt) { + await new Promise(r => Event.once((subject.result as LiveTestResult).onComplete)(r)); + } + + const followups = await this.testService.provideTestFollowups({ + extId: subject.test.extId, + messageIndex: subject.messageIndex, + resultId: subject.result.id, + taskIndex: subject.taskIndex, + }, cts.token); + + + if (!followups.followups.length || cts.token.isCancellationRequested) { + followups.dispose(); + return; + } + + this.visibleStore.add(followups); + + dom.clearNode(this.el.root); + this.el.root.classList.toggle('animated', Date.now() - start > FOLLOWUP_ANIMATION_MIN_TIME); + + this.el.root.appendChild(this.makeFollowupLink(followups.followups[0])); + if (followups.followups.length > 1) { + this.el.root.appendChild(this.makeMoreLink(followups.followups)); + } + + this.container.appendChild(this.el.root); + this.visibleStore.add(toDisposable(() => { + this.el.root.remove(); + })); + } + + private makeFollowupLink(first: ITestFollowup) { + const link = this.makeLink(() => this.actionFollowup(link, first)); + dom.reset(link, ...renderLabelWithIcons(first.message)); + return link; + } + + private makeMoreLink(followups: ITestFollowup[]) { + const link = this.makeLink(() => + this.quickInput.pick(followups.map((f, i) => ({ + label: f.message, + index: i + }))).then(picked => { + if (picked?.length) { + followups[picked[0].index].execute(); + } + }) + ); + + link.innerText = localize('testFollowup.more', '+{0} More...', followups.length - 1); + return link; + } + + private makeLink(onClick: () => void) { + const link = document.createElement('a'); + link.tabIndex = 0; + this.visibleStore.add(dom.addDisposableListener(link, 'click', onClick)); + this.visibleStore.add(dom.addDisposableListener(link, 'keydown', e => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { + onClick(); + } + })); + + return link; + } + + private actionFollowup(link: HTMLAnchorElement, fu: ITestFollowup) { + if (link.ariaDisabled !== 'true') { + link.ariaDisabled = 'true'; + fu.execute(); + + if (this.editor) { + TestingOutputPeekController.get(this.editor)?.removePeek(); + } + } + } +} diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 0012ae4a1c5..ca336ec5632 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -4,13 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; import { IAction } from 'vs/base/common/actions'; -import { Limiter } from 'vs/base/common/async'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -18,7 +13,7 @@ import { stripIcons } from 'vs/base/common/iconLabels'; import { Iterable } from 'vs/base/common/iterator'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; -import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { count } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./testingOutputPeek'; @@ -35,7 +30,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IPeekViewService, PeekViewWidget, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView'; import { localize, localize2 } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { FloatingClickMenu } from 'vs/platform/actions/browser/floatingMenu'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Action2, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -50,7 +44,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; @@ -58,17 +51,16 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; -import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput'; -import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, equalsSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; -import { OutputPeekTree } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree'; +import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { TestResultsViewContent } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent'; import { testingMessagePeekBorder, testingPeekBorder, testingPeekHeaderBackground, testingPeekMessageHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme'; import { AutoOpenPeekViewWhen, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; -import { IObservableValue, MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; +import { MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; -import { ITestResult, LiveTestResult, TestResultItemChange, TestResultItemChangeReason, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestResult, TestResultItemChange, TestResultItemChangeReason, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService, ResultChangeEvent } from 'vs/workbench/contrib/testing/common/testResultService'; -import { ITestFollowup, ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; import { IRichLocation, ITestMessage, TestMessageType, TestResultItem } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { IShowResultOptions, ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener'; @@ -403,7 +395,6 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener } } - /** * Adds output/message peek functionality to code editors. */ @@ -661,278 +652,6 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo } } -const FOLLOWUP_ANIMATION_MIN_TIME = 500; - -class FollowupActionWidget extends Disposable { - private readonly el = dom.h('div.testing-followup-action', []); - private readonly visibleStore = this._register(new DisposableStore()); - - constructor( - private readonly container: HTMLElement, - private readonly editor: ICodeEditor | undefined, - @ITestService private readonly testService: ITestService, - @IQuickInputService private readonly quickInput: IQuickInputService, - ) { - super(); - } - - public show(subject: InspectSubject) { - this.visibleStore.clear(); - if (subject instanceof MessageSubject) { - this.showMessage(subject); - } - } - - private async showMessage(subject: MessageSubject) { - const cts = this.visibleStore.add(new CancellationTokenSource()); - const start = Date.now(); - - // Wait for completion otherwise results will not be available to the ext host: - if (subject.result instanceof LiveTestResult && !subject.result.completedAt) { - await new Promise(r => Event.once((subject.result as LiveTestResult).onComplete)(r)); - } - - const followups = await this.testService.provideTestFollowups({ - extId: subject.test.extId, - messageIndex: subject.messageIndex, - resultId: subject.result.id, - taskIndex: subject.taskIndex, - }, cts.token); - - - if (!followups.followups.length || cts.token.isCancellationRequested) { - followups.dispose(); - return; - } - - this.visibleStore.add(followups); - - dom.clearNode(this.el.root); - this.el.root.classList.toggle('animated', Date.now() - start > FOLLOWUP_ANIMATION_MIN_TIME); - - this.el.root.appendChild(this.makeFollowupLink(followups.followups[0])); - if (followups.followups.length > 1) { - this.el.root.appendChild(this.makeMoreLink(followups.followups)); - } - - this.container.appendChild(this.el.root); - this.visibleStore.add(toDisposable(() => { - this.el.root.remove(); - })); - } - - private makeFollowupLink(first: ITestFollowup) { - const link = this.makeLink(() => this.actionFollowup(link, first)); - dom.reset(link, ...renderLabelWithIcons(first.message)); - return link; - } - - private makeMoreLink(followups: ITestFollowup[]) { - const link = this.makeLink(() => - this.quickInput.pick(followups.map((f, i) => ({ - label: f.message, - index: i - }))).then(picked => { - if (picked?.length) { - followups[picked[0].index].execute(); - } - }) - ); - - link.innerText = localize('testFollowup.more', '+{0} More...', followups.length - 1); - return link; - } - - private makeLink(onClick: () => void) { - const link = document.createElement('a'); - link.tabIndex = 0; - this.visibleStore.add(dom.addDisposableListener(link, 'click', onClick)); - this.visibleStore.add(dom.addDisposableListener(link, 'keydown', e => { - const event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { - onClick(); - } - })); - - return link; - } - - private actionFollowup(link: HTMLAnchorElement, fu: ITestFollowup) { - if (link.ariaDisabled !== 'true') { - link.ariaDisabled = 'true'; - fu.execute(); - - if (this.editor) { - TestingOutputPeekController.get(this.editor)?.removePeek(); - } - } - } -} - -class TestResultsViewContent extends Disposable { - private static lastSplitWidth?: number; - - private readonly didReveal = this._register(new Emitter<{ subject: InspectSubject; preserveFocus: boolean }>()); - private readonly currentSubjectStore = this._register(new DisposableStore()); - private followupWidget!: FollowupActionWidget; - private messageContextKeyService!: IContextKeyService; - private contextKeyTestMessage!: IContextKey; - private contextKeyResultOutdated!: IContextKey; - - private dimension?: dom.Dimension; - private splitView!: SplitView; - private messageContainer!: HTMLElement; - private contentProviders!: IPeekOutputRenderer[]; - private contentProvidersUpdateLimiter = this._register(new Limiter(1)); - - public current?: InspectSubject; - - /** Fired when a tree item is selected. Populated only on .fillBody() */ - public onDidRequestReveal!: Event; - - constructor( - private readonly editor: ICodeEditor | undefined, - private readonly options: { - historyVisible: IObservableValue; - showRevealLocationOnMessages: boolean; - locationForProgress: string; - }, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextModelService protected readonly modelService: ITextModelService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - ) { - super(); - } - - public fillBody(containerElement: HTMLElement): void { - const initialSpitWidth = TestResultsViewContent.lastSplitWidth; - this.splitView = new SplitView(containerElement, { orientation: Orientation.HORIZONTAL }); - - const { historyVisible, showRevealLocationOnMessages } = this.options; - const isInPeekView = this.editor !== undefined; - const messageContainer = this.messageContainer = dom.append(containerElement, dom.$('.test-output-peek-message-container')); - this.followupWidget = this._register(this.instantiationService.createInstance(FollowupActionWidget, messageContainer, this.editor)); - this.contentProviders = [ - this._register(this.instantiationService.createInstance(DiffContentProvider, this.editor, messageContainer)), - this._register(this.instantiationService.createInstance(MarkdownTestMessagePeek, messageContainer)), - this._register(this.instantiationService.createInstance(TerminalMessagePeek, messageContainer, isInPeekView)), - this._register(this.instantiationService.createInstance(PlainTextMessagePeek, this.editor, messageContainer)), - ]; - - this.messageContextKeyService = this._register(this.contextKeyService.createScoped(containerElement)); - this.contextKeyTestMessage = TestingContextKeys.testMessageContext.bindTo(this.messageContextKeyService); - this.contextKeyResultOutdated = TestingContextKeys.testResultOutdated.bindTo(this.messageContextKeyService); - - const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree')); - const tree = this._register(this.instantiationService.createInstance( - OutputPeekTree, - treeContainer, - this.didReveal.event, - { showRevealLocationOnMessages, locationForProgress: this.options.locationForProgress }, - )); - - this.onDidRequestReveal = tree.onDidRequestReview; - - this.splitView.addView({ - onDidChange: Event.None, - element: messageContainer, - minimumSize: 200, - maximumSize: Number.MAX_VALUE, - layout: width => { - TestResultsViewContent.lastSplitWidth = width; - if (this.dimension) { - for (const provider of this.contentProviders) { - provider.layout({ height: this.dimension.height, width }); - } - } - }, - }, Sizing.Distribute); - - this.splitView.addView({ - onDidChange: Event.None, - element: treeContainer, - minimumSize: 100, - maximumSize: Number.MAX_VALUE, - layout: width => { - if (this.dimension) { - tree.layout(this.dimension.height, width); - } - }, - }, Sizing.Distribute); - - const historyViewIndex = 1; - this.splitView.setViewVisible(historyViewIndex, historyVisible.value); - this._register(historyVisible.onDidChange(visible => { - this.splitView.setViewVisible(historyViewIndex, visible); - })); - - if (initialSpitWidth) { - queueMicrotask(() => this.splitView.resizeView(0, initialSpitWidth)); - } - } - - /** - * Shows a message in-place without showing or changing the peek location. - * This is mostly used if peeking a message without a location. - */ - public reveal(opts: { subject: InspectSubject; preserveFocus: boolean }) { - this.didReveal.fire(opts); - - if (this.current && equalsSubject(this.current, opts.subject)) { - return Promise.resolve(); - } - - this.current = opts.subject; - return this.contentProvidersUpdateLimiter.queue(async () => { - await Promise.all(this.contentProviders.map(p => p.update(opts.subject))); - this.followupWidget.show(opts.subject); - this.currentSubjectStore.clear(); - this.populateFloatingClick(opts.subject); - }); - } - - private populateFloatingClick(subject: InspectSubject) { - if (!(subject instanceof MessageSubject)) { - return; - } - - this.currentSubjectStore.add(toDisposable(() => { - this.contextKeyResultOutdated.reset(); - this.contextKeyTestMessage.reset(); - })); - - this.contextKeyTestMessage.set(subject.contextValue || ''); - if (subject.result instanceof LiveTestResult) { - this.contextKeyResultOutdated.set(subject.result.getStateById(subject.test.extId)?.retired ?? false); - this.currentSubjectStore.add(subject.result.onChange(ev => { - if (ev.item.item.extId === subject.test.extId) { - this.contextKeyResultOutdated.set(ev.item.retired ?? false); - } - })); - } else { - this.contextKeyResultOutdated.set(true); - } - - - const instaService = this.currentSubjectStore.add(this.instantiationService - .createChild(new ServiceCollection([IContextKeyService, this.messageContextKeyService]))); - - this.currentSubjectStore.add(instaService.createInstance(FloatingClickMenu, { - container: this.messageContainer, - menuId: MenuId.TestMessageContent, - getActionArg: () => (subject as MessageSubject).context, - })); - } - - public onLayoutBody(height: number, width: number) { - this.dimension = new dom.Dimension(width, height); - this.splitView.layout(width); - } - - public onWidth(width: number) { - this.splitView.layout(width); - } -} class TestResultsPeek extends PeekViewWidget { private static lastHeightInLines?: number; From ad476c34165861460f13b0eee011a6c07393a345 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 8 Jul 2024 16:07:00 -0700 Subject: [PATCH 011/798] Bump that distro (#221226) :drumroll: --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81a0e0d416d..b99927cdeef 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.92.0", - "distro": "58e7d90e05684b6937db21dd372f7a088bdc9dc1", + "distro": "6e11724ed97152dcd4510f2520755c983a6f439c", "author": { "name": "Microsoft Corporation" }, From b23e791eb5afbd95f05aa24da7693ce89344a079 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 8 Jul 2024 17:40:28 -0700 Subject: [PATCH 012/798] Use AzureLogin task for latest-release-monitor (#221194) --- .github/workflows/latest-release-monitor.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index f7392dc24a8..7db2cedf9df 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -1,4 +1,9 @@ name: Latest Release Monitor + +permissions: + id-token: write + contents: read + on: schedule: - cron: 0/5 * * * * @@ -8,7 +13,13 @@ on: jobs: main: runs-on: ubuntu-latest + environment: main steps: + - uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + allow-no-subscriptions: true - name: Checkout Actions uses: actions/checkout@v4 with: @@ -22,6 +33,4 @@ jobs: - name: Run Latest Release Monitor uses: ./actions/latest-release-monitor with: - storageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} From 2fe05272052cf1f0ab2133f7f4874e9a748e8e74 Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 9 Jul 2024 14:33:22 +0900 Subject: [PATCH 013/798] chore: update to electron 30 (#215406) * chore: update electron@30.0.9 * chore: update rpm deps * chore: bump electron@30.1.2 * fix: update kerberos for Node.js 20.x Refs https://github.com/mongodb-js/kerberos/commit/c1f7acafb211d1b449086433578495d4ae0b869f * fix: use shell when spawning .bat or .cmd files Refs https://github.com/nodejs/node/commit/6627222409 * fix: update @vscode/test-electron@2.4.0 Refs https://github.com/microsoft/vscode-test/commit/3f7a3cc5c537957d55fa9e6aeab9d860f7a60078 * fixup! use shell when spawning .bat or .cmd files * chore: bump nodejs@20.14.0 internal build * ci: skip nodejsMirror for 20.14.0 due to missing builds * fixup! use shell when spawning .bat or .cmd files * chore: update debian deps * fixup! skip nodejsMirror for 20.14.0 due to missing builds * fix: universal build - Updates vscode-universal-bundler to support x64ArchFiles option - Kerberos starts building universal binaries which should now be skipped from lipo step via x64ArchFiles - Skips bundling *.mk files * chore: bump distro --- .nvmrc | 2 +- .yarnrc | 4 +- .../alpine/cli-build-alpine.yml | 2 +- .../darwin/cli-build-darwin.yml | 2 +- .../darwin/product-build-darwin-cli-sign.yml | 2 +- .../darwin/product-build-darwin-sign.yml | 2 +- .../darwin/product-build-darwin-universal.yml | 2 +- .../darwin/product-build-darwin.yml | 2 +- build/azure-pipelines/linux/setup-env.sh | 8 +- build/checksums/electron.txt | 150 ++++++------- build/checksums/nodejs.txt | 14 +- build/darwin/create-universal-app.js | 31 +-- build/darwin/create-universal-app.ts | 32 +-- build/gulpfile.reh.js | 41 ++-- build/gulpfile.vscode.js | 2 + build/lib/asar.js | 13 +- build/lib/asar.ts | 14 +- build/linux/debian/dep-lists.js | 3 + build/linux/debian/dep-lists.ts | 3 + build/linux/dependencies-generator.js | 2 +- build/linux/dependencies-generator.ts | 2 +- build/linux/rpm/dep-lists.js | 5 + build/linux/rpm/dep-lists.ts | 5 + build/package.json | 2 +- build/yarn.lock | 169 ++++++++------- cgmanifest.json | 14 +- .../vscode-test-resolver/src/extension.ts | 8 +- package.json | 8 +- remote/.yarnrc | 4 +- remote/package.json | 2 +- remote/yarn.lock | 28 +-- src/main.js | 4 +- test/automation/src/playwrightBrowser.ts | 3 +- test/integration/browser/src/index.ts | 4 +- yarn.lock | 198 ++++++++++++------ 35 files changed, 465 insertions(+), 322 deletions(-) diff --git a/.nvmrc b/.nvmrc index bc78e9f2695..48b14e6b2b5 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.12.1 +20.14.0 diff --git a/.yarnrc b/.yarnrc index b153fa4724f..a94956590fa 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "29.4.0" -ms_build_id "9728852" +target "30.1.2" +ms_build_id "9759760" runtime "electron" build_from_source "true" diff --git a/build/azure-pipelines/alpine/cli-build-alpine.yml b/build/azure-pipelines/alpine/cli-build-alpine.yml index a6442dfe290..2c3b653ce7e 100644 --- a/build/azure-pipelines/alpine/cli-build-alpine.yml +++ b/build/azure-pipelines/alpine/cli-build-alpine.yml @@ -16,7 +16,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: # Install yarn as the ARM64 build agent is using vanilla Ubuntu diff --git a/build/azure-pipelines/darwin/cli-build-darwin.yml b/build/azure-pipelines/darwin/cli-build-darwin.yml index 1d8dffc464d..ad901c8de1d 100644 --- a/build/azure-pipelines/darwin/cli-build-darwin.yml +++ b/build/azure-pipelines/darwin/cli-build-darwin.yml @@ -16,7 +16,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../cli/cli-apply-patches.yml@self diff --git a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml index 80e90a52bac..e6cff11dc7d 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml @@ -9,7 +9,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - script: node build/setup-npm-registry.js $NPM_REGISTRY build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index fb8cf8341c3..e8f1bdf511c 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -3,7 +3,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - task: UseDotNet@2 inputs: diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml index f8b201f40d4..45367dde187 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-universal.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -3,7 +3,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - template: ../distro/download-distro.yml@self diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 11f69d735ac..a49d5e4abd6 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -20,7 +20,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../distro/download-distro.yml@self diff --git a/build/azure-pipelines/linux/setup-env.sh b/build/azure-pipelines/linux/setup-env.sh index 9bfbf9ab41a..6f3f0100867 100755 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh @@ -13,7 +13,7 @@ SYSROOT_ARCH="$SYSROOT_ARCH" node -e '(async () => { const { getVSCodeSysroot } if [ "$npm_config_arch" == "x64" ]; then if [ "$(echo "$@" | grep -c -- "--only-remote")" -eq 0 ]; then # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/122.0.6261.156/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + curl -s https://raw.githubusercontent.com/chromium/chromium/124.0.6367.243/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux # Download libcxx headers and objects from upstream electron releases DEBUG=libcxx-fetcher \ @@ -25,9 +25,9 @@ if [ "$npm_config_arch" == "x64" ]; then # Set compiler toolchain # Flags for the client build are based on - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:build/config/c++/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/c++/BUILD.gn export CC="$PWD/.build/CR_Clang/bin/clang --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index a80aa1531f1..52950abd800 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -3d3d8bb185d7b63b0db910661fdd69d6381afb8c97742bbd2526a9c932e1f8ca *chromedriver-v29.4.0-darwin-arm64.zip -c3d075943d87604ffa50382cc8d5798485349544ca391cab88c892f889d3b14c *chromedriver-v29.4.0-darwin-x64.zip -6d62d2dba55e4419fa003d45f93dad1324ec29a4d3eb84fd9fd5fd7a64339389 *chromedriver-v29.4.0-linux-arm64.zip -81bb3d362331c7296f700b1b0e8f07c4c7739b1151f698cd56af927bedda59e7 *chromedriver-v29.4.0-linux-armv7l.zip -ab593cc39aefac8c5abd259e31f6add4b2b70c52231724a6c08ac1872b4a0edf *chromedriver-v29.4.0-linux-x64.zip -705d42ccc05b2c48b0673b9dcf63eb78772bb79dba078a523d384ed2481bc9c0 *chromedriver-v29.4.0-mas-arm64.zip -956a7caa28eeeb0c02eb7638a53215ffd89b4f12880f0893ff10f497ca1a8117 *chromedriver-v29.4.0-mas-x64.zip -1f070176aa33e0139d61a3d758fd2f015f09bb275577293fe93564749b6310ba *chromedriver-v29.4.0-win32-arm64.zip -38a71526d243bcb73c28cb648bd4816d70b5e643df52f9f86a83416014589744 *chromedriver-v29.4.0-win32-ia32.zip -f90750d3589cb3c9f6f0ebc70d5e025cf81c382e8c23fa47a54570696a478ef0 *chromedriver-v29.4.0-win32-x64.zip -05dffc90dd1341cc7a6b50127985e4e217fef7f50a173c7d0ff34039dd2d81b6 *electron-api.json -7f63f7cf675ba6dec3a5e4173d729bd53c75f81e612f809641d9d0c4d9791649 *electron-v29.4.0-darwin-arm64-dsym-snapshot.zip -aa29530fcafa4db364978d4f414a6ec2005ea695f7fee70ffbe5e114e9e453f0 *electron-v29.4.0-darwin-arm64-dsym.zip -8d12fb6d9bcdf5bbfc93dbcd1cac348735dc6f98aa450ee03ec7837a01a8a938 *electron-v29.4.0-darwin-arm64-symbols.zip -c16d05f1231bb3c77da05ab236b454b3a2b6a642403be51e7c9b16cd2c421a19 *electron-v29.4.0-darwin-arm64.zip -2dfc1017831ab2f6e9ddb575d3b9cff5a0d56f16a335a3c0df508e964e2db963 *electron-v29.4.0-darwin-x64-dsym-snapshot.zip -025de6aa39d98762928e1b700f46177e74be20101b27457659b938e2c69db326 *electron-v29.4.0-darwin-x64-dsym.zip -ec4eb0a618207233985ceaab297be34b3d4f0813d88801d5637295b238dd661a *electron-v29.4.0-darwin-x64-symbols.zip -8ed7924f77a5c43c137a57097c5c47c2e8e9a78197e18af11a767c98035c123e *electron-v29.4.0-darwin-x64.zip -bde1772fa8ac4850e108012a9edd3bd93472bad8f68ddd55fca355dad81dde4f *electron-v29.4.0-linux-arm64-debug.zip -dfe7852a7423196efb2205c788d942db3ffc9de6ce52577e173bcf7ca6973d48 *electron-v29.4.0-linux-arm64-symbols.zip -c3764d6c3799950e3418e8e5a5a5b2c41abe421dd8bcdebf054c7c85798d9860 *electron-v29.4.0-linux-arm64.zip -bde1772fa8ac4850e108012a9edd3bd93472bad8f68ddd55fca355dad81dde4f *electron-v29.4.0-linux-armv7l-debug.zip -360668ba669cb2c01c2f960cdee76c29670e6ce907ccc0718e971a04af594ce9 *electron-v29.4.0-linux-armv7l-symbols.zip -c5e92943ad78b4e41a32ae53c679e148ea2ae09f95f914b1834dbdbae578ba91 *electron-v29.4.0-linux-armv7l.zip -375be885426bcbd272bd068bfcef41a83296c2f8e61e633233d2a9e9a69242fc *electron-v29.4.0-linux-x64-debug.zip -847e0f75624616c2918b33de2eefeec63419bd250685610d3f52fa115527d2b9 *electron-v29.4.0-linux-x64-symbols.zip -91e5eb374c2c85a07c2d4e99a89eb18515ff0169a49c3fa75289800e1225729e *electron-v29.4.0-linux-x64.zip -098f973537c3d9679a69409d0b84bcc1a6113bb2002ee60068e2c22f335a3855 *electron-v29.4.0-mas-arm64-dsym-snapshot.zip -2724aa32eb441eea21680d95fc1efdd75ac473fa19623c7acf3d546419e96154 *electron-v29.4.0-mas-arm64-dsym.zip -98dd81914752a57da4cbaad1f0aa94b16335f9b8f997be9aa049be90b96b2886 *electron-v29.4.0-mas-arm64-symbols.zip -fd2663f65c1f995304e3eb65870b7146adfefef07cf82bf44de75855fd4f36e8 *electron-v29.4.0-mas-arm64.zip -237983b2169e69bb73aa0987e871e3e486755904b71ebe36c3e902377f92754a *electron-v29.4.0-mas-x64-dsym-snapshot.zip -a5d59599827d32ef322b99eee8416e39235f4c7a0ada78342a885665e0b732dd *electron-v29.4.0-mas-x64-dsym.zip -5182e7697ac0591e0b95c33f70316af24093c9100f442be2cee0039660e959ac *electron-v29.4.0-mas-x64-symbols.zip -e0ee7057aff0240a70b9ed75ff44d55aeae9af67fbc8915f741711a8bb6fe744 *electron-v29.4.0-mas-x64.zip -2802872dfc6de0f0e2e8cef9d2f4f384e3d82b20ad36fc981c4e725dd2f2abcd *electron-v29.4.0-win32-arm64-pdb.zip -d49c954dc25ae9e4c75e61af80b9718014c52f016f43a29071913f0e7100c7bd *electron-v29.4.0-win32-arm64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v29.4.0-win32-arm64-toolchain-profile.zip -483d692efbe4fb1231ff63afb8a236b2e22b486fbe5ac6abbc8b208abf94a4d3 *electron-v29.4.0-win32-arm64.zip -98458f49ba67a08e473d475a68a2818d9df076a5246fbc9b45403e8796f9d35b *electron-v29.4.0-win32-ia32-pdb.zip -69d505d4ae59d9dddf83c4e530e45dd7c5bc64d6da90cf4f851e523be9e51014 *electron-v29.4.0-win32-ia32-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v29.4.0-win32-ia32-toolchain-profile.zip -d5a21a17a64e9638f49f057356af23b51f56bd6a7fea3c2e0a28ff3186a7bc41 *electron-v29.4.0-win32-ia32.zip -521ee7b3398c4dc395b43dac86cd099e86a6123de2b43636ee805b7da014ed3f *electron-v29.4.0-win32-x64-pdb.zip -e33848ebd6c6e4ce431aa367bef887050947a136e883677cfc524ca5cabc1e98 *electron-v29.4.0-win32-x64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v29.4.0-win32-x64-toolchain-profile.zip -e4ef85aa3608221f8a3e011c1b1c2d2d36093ad19bda12d16b3816929fb6c99b *electron-v29.4.0-win32-x64.zip -707ee08593289ee83514b4fc55123611309f995788f38a5ec03e285741aac1c8 *electron.d.ts -281b5f4a49de55fdb86b1662530f07f2ced1252c878eb7a941c88ede545339e0 *ffmpeg-v29.4.0-darwin-arm64.zip -0b735912df9b2ff3d03eb23942e03bc0116d82f1291d0a45cbde14177c2f3066 *ffmpeg-v29.4.0-darwin-x64.zip -4e2ba537d7c131abbd34168bce2c28cc9ef6262b217d5f4085afccfdf9635da6 *ffmpeg-v29.4.0-linux-arm64.zip -4aa56ad5d849f4e61af22678a179346b68aec9100282e1b8a43df25d95721677 *ffmpeg-v29.4.0-linux-armv7l.zip -0558e6e1f78229d303e16d4d8c290794baa9adc619fdd2ddccadb3ea241a1df4 *ffmpeg-v29.4.0-linux-x64.zip -224f15d8f96c75348cd7f1b85c4eab63468fae1e50ff4b1381e08011cf76e4f7 *ffmpeg-v29.4.0-mas-arm64.zip -175ec79f0dc4c5966d9a0ca6ec1674106340ecc64503585c12c2f854249af06f *ffmpeg-v29.4.0-mas-x64.zip -5fa13744b87fef1bfd24a37513677f446143e085504541f8ce97466803bd1893 *ffmpeg-v29.4.0-win32-arm64.zip -d7ba316bb7e13025c9db29e0acafebb540b7716c9f111e469733615d8521186a *ffmpeg-v29.4.0-win32-ia32.zip -35c70a28bcfd4f0b1f8c985d3d1348936bd60767d231ce28ba38f3daeeef64bb *ffmpeg-v29.4.0-win32-x64.zip -8c7228ea0ecab25a1f7fcd1ba9680684d19f9671a497113d71a851a53867b048 *hunspell_dictionaries.zip -7552547c8d585b9bc43518d239d7ce3ad7c5cad0346b07cdcfc1eab638b2b794 *libcxx-objects-v29.4.0-linux-arm64.zip -76054a779d4845ad752b625213ce8990f08dcc5b89aa20660dd4f2e817ba30a8 *libcxx-objects-v29.4.0-linux-armv7l.zip -761c317a9c874bd3d1118d0ecad33c4be23727f538cfbb42a08dd87c68da6039 *libcxx-objects-v29.4.0-linux-x64.zip -f98f9972cc30200b8e05815f5a9cd5cec04bdeee0e48ae2143cdaeff5db9d71d *libcxx_headers.zip -f0b0dd2be579baaf97901322ef489d03fae69a0b8524ea77b24fb3c896f73dd9 *libcxxabi_headers.zip -5da864ea23d70538298a40e0d037a5a461a6b74984e72fd4f0cd20904bccaed1 *mksnapshot-v29.4.0-darwin-arm64.zip -bde97bd7c69209ed6bf4cf1cdf7de622e3a9f50fe6b4dc4b5618eee868f47c62 *mksnapshot-v29.4.0-darwin-x64.zip -a3df9b9e6ef14efe5827d0256d8ecaebe6d8be130cfc3faac0dea76eb53b9b11 *mksnapshot-v29.4.0-linux-arm64-x64.zip -648b9dbca21194d663ddb706e6086a166e691263c764c80f836ae02c27e3657a *mksnapshot-v29.4.0-linux-armv7l-x64.zip -e7a4201cda3956380facc2b5b9d0b1020cc5e654fba44129fc7429a982411cc1 *mksnapshot-v29.4.0-linux-x64.zip -ffb44c45733675e0378f45fce25dafa95697d0c86179f8e46742ada16bc11aa1 *mksnapshot-v29.4.0-mas-arm64.zip -0242da3ca193206e56b88eb108502244bae35dcc587210bd0a32d9fa4cb71041 *mksnapshot-v29.4.0-mas-x64.zip -1445806dca6effbc60072bbde7997cefb62bdb7a9e295a090d26f27c3882685f *mksnapshot-v29.4.0-win32-arm64-x64.zip -09599adc3afb0a13ae87fc4b8ab97c729fe3689faa6a4f5f7a4a3cf0d9cc49d3 *mksnapshot-v29.4.0-win32-ia32.zip -84f80683d95665d29284386509bb104e840ff0b797bfbbd19da86b84d370aa49 *mksnapshot-v29.4.0-win32-x64.zip +cdf0522dacc5fdf75a9a4ca9a20f049793ef8bae2b04e37f02e8923fcecd4c76 *chromedriver-v30.1.2-darwin-arm64.zip +f739afd34c48aae18da1d9dffb2332f0c2b2e27ff2056ac619e0a8c9414618f0 *chromedriver-v30.1.2-darwin-x64.zip +c2a220a316c268984bb8135975f28adecc392cf5cd2244af8cc21d60018a6a10 *chromedriver-v30.1.2-linux-arm64.zip +fad358f076caff4eecc3d8b63cea2e109cc0ff8b4632bb4edd21a7c7721bc428 *chromedriver-v30.1.2-linux-armv7l.zip +addc230541e9ee44b311fba9e900c5cb7d8b4d64b79a6d5dae68a71ced1c4611 *chromedriver-v30.1.2-linux-x64.zip +e2d0876fac8af41e0dd9c1b14c5315b426c55217e54e2b4ca5e28faae1b19557 *chromedriver-v30.1.2-mas-arm64.zip +c705d0b74a4658c197a87ed1e9e2509e55186769376b40493ec68b7cbb36c312 *chromedriver-v30.1.2-mas-x64.zip +8bbf9ccb789b236dec3e871d2499c14926a4c2dd3c865f8c7316ba3aa5b3f58f *chromedriver-v30.1.2-win32-arm64.zip +75af4b07bbd3a8fd7e18d63eb936e11054d01b52d438e4f7b7c5e6b82a41dec3 *chromedriver-v30.1.2-win32-ia32.zip +2d306df2c66314be12df78b6139ebf2d616463efdf4017473330d87a61954c3a *chromedriver-v30.1.2-win32-x64.zip +1990fe83ca25b990773ce099b90fb804fc026c1398279731229ba37d02c23000 *electron-api.json +64360b0db764c1bf16a8ab810a25c03f68873691d2897b360281bc50a645b6bb *electron-v30.1.2-darwin-arm64-dsym-snapshot.zip +3192307419ee2bafc3c99c62d79dbd2e6cba5815ae245be994f0b1e1d7fedb46 *electron-v30.1.2-darwin-arm64-dsym.zip +823eadc46a498af7433dae2ace1c8f0b2b0c8cbf98204fed0fdce8110ddafbc1 *electron-v30.1.2-darwin-arm64-symbols.zip +3c651624b1605411e595a6e9f6e874effb947c80eda4b8d0bb7d2972ff6ff242 *electron-v30.1.2-darwin-arm64.zip +fa5cbbc3e7760907229fa0753c3faa2b43e09bcb987b6c3f693e0030aa65e62b *electron-v30.1.2-darwin-x64-dsym-snapshot.zip +d97087f4c7e41fd67b2f0f7aa623ccf1effdbb94133b883dbae1e4c42a576b03 *electron-v30.1.2-darwin-x64-dsym.zip +a129109ed6ca23f66d625ebff9e57be117bc0a32c4f4348e78c1ad7dd41c4189 *electron-v30.1.2-darwin-x64-symbols.zip +8645e10af9b047c765a6cc880f9fa53f266e618569eaf65c0ea9fa1058be20f7 *electron-v30.1.2-darwin-x64.zip +afa016399f57bbbb658238dd715ef2a66790602ff46514e9cd99f2e078789c7d *electron-v30.1.2-linux-arm64-debug.zip +aec05a3e46a83d7c3e502b04245d3d6d2db8d5789a4dcc991f7cf6e7e3cd7036 *electron-v30.1.2-linux-arm64-symbols.zip +953c51413abfd62efdba070f99c961202ce5ef5e77c5cee5eaaf097ac2f5bc9b *electron-v30.1.2-linux-arm64.zip +afa016399f57bbbb658238dd715ef2a66790602ff46514e9cd99f2e078789c7d *electron-v30.1.2-linux-armv7l-debug.zip +04a6851e218c9a6f70870a341beea1b194a94d2d78f3a283065a34654cce7e30 *electron-v30.1.2-linux-armv7l-symbols.zip +677d6b4e6721ffb27b0037628c235cd0a9f10104beabb2b6c67d4b0328a9d001 *electron-v30.1.2-linux-armv7l.zip +1b6d8926e2c7cacbb33e56259ebe908c52e2a6dbbe37f0def043121f228a3a37 *electron-v30.1.2-linux-x64-debug.zip +dc4b526b02ec028d20836ee4617335d0569170846959761c3e8dc615d668f596 *electron-v30.1.2-linux-x64-symbols.zip +724aeca4b2f428f544e9b7e5e52e2074458c2e198f588530819cd0318af8599d *electron-v30.1.2-linux-x64.zip +15b10a6cf6a1ea029792282088647abbf58b415fdb4dd004c2c67c4a8f216ef3 *electron-v30.1.2-mas-arm64-dsym-snapshot.zip +00dbfc36c8ff6ecfbb8b01b51fee41f876bf3456017a8cc694a0c56608061f5e *electron-v30.1.2-mas-arm64-dsym.zip +e402b68cad20ee60cce376dacf0e2e72f1fcd0b6359dda7789626dca4b101e8b *electron-v30.1.2-mas-arm64-symbols.zip +8e59fc7c6df96a029310d6f7769e0c76592dc746d31764b35460de129231d12b *electron-v30.1.2-mas-arm64.zip +e8bfe6b58d2767dd52a7668df380be9c786abed0b25d642bee70c278758d2e77 *electron-v30.1.2-mas-x64-dsym-snapshot.zip +418f32558f9a107ebc942666a6ca680874db8ed438ae3f0064255abe0f9ce77e *electron-v30.1.2-mas-x64-dsym.zip +6d05da37cb39c664a764c879216e762efdb66f97734e42bbaa8f115b11fd3c87 *electron-v30.1.2-mas-x64-symbols.zip +af7b85a28593227add7e595e01d570e19512b40a29599ec007ef7cd4b5a11435 *electron-v30.1.2-mas-x64.zip +580c9b9fee6bacfdc4d3a1118953ba0096bcc19d28b1d804d72d40c5caac8d81 *electron-v30.1.2-win32-arm64-pdb.zip +dbb0ce79933c3688b7fa2bf04ce083d6e8da0a8c07b5104f53805e2e92679cd4 *electron-v30.1.2-win32-arm64-symbols.zip +7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.1.2-win32-arm64-toolchain-profile.zip +1b9035b541999e0cedcfe3893bb3c1497435494279d599c4e9300fe204f4d560 *electron-v30.1.2-win32-arm64.zip +3cb6869a69d118488dc48ed573b3fc9445bea33cf0a04338ca1b8000b4eaf516 *electron-v30.1.2-win32-ia32-pdb.zip +3f353849ef21506d5ca7a2c26df84d7c744ef1795acd33307f501764dfbe9bc1 *electron-v30.1.2-win32-ia32-symbols.zip +7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.1.2-win32-ia32-toolchain-profile.zip +431aff46eca9ddb726af3c8d6f4bbb72158e5c872c1b8bee221b3df0a8e94947 *electron-v30.1.2-win32-ia32.zip +85428715d302d4c97e47ca0b6409497191846baccbc36debf895cde724f9445f *electron-v30.1.2-win32-x64-pdb.zip +a5156829bc0caab5c70e6cd352941b6fb7b1e396d7869298c1ceaa69d742e3dc *electron-v30.1.2-win32-x64-symbols.zip +7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.1.2-win32-x64-toolchain-profile.zip +d400ed1aca2b7b1093aee8b5a8544b112e9f81a570cf77f6bdfe019ceb003f7f *electron-v30.1.2-win32-x64.zip +3187cdd642b968e17a768a3fdaff44bedb69954be3d88e7ad55aac9787b70485 *electron.d.ts +4c8bd4215102f563cf0464ae4edd4022921c53e0ebd4bb6b6f7f7434d50081aa *ffmpeg-v30.1.2-darwin-arm64.zip +2b8c57755ccb64a64540433d3cfbfecd29d07e28bf23c5f08a2edd0e2333a645 *ffmpeg-v30.1.2-darwin-x64.zip +c6f69859dc2ca04daa7c218936e5bc044fc19582423db954c5e8664ce7f331df *ffmpeg-v30.1.2-linux-arm64.zip +12f760ce312e4ee98ddd9496600ca9a1468e28f0ed6d41c6b9284dec841f550f *ffmpeg-v30.1.2-linux-armv7l.zip +c7207cc057849033d550d6897a53f7abee455f31b8f571c3c57a01f2871ff5af *ffmpeg-v30.1.2-linux-x64.zip +447b19465677636261ce47cc256f221c660e66cd136b33c3c742b8d23481b924 *ffmpeg-v30.1.2-mas-arm64.zip +e44fcdb5d0e62d328c0bb5ebeed54f388b09591a33e4dc1698c421e4c9d881dc *ffmpeg-v30.1.2-mas-x64.zip +b470cc217c4f06b3fe4edf6695b4762e7c926d07c1f41c3cab0ddb1d71ce8ce4 *ffmpeg-v30.1.2-win32-arm64.zip +92f1743c16210a77d07e9553cf96ccfb4e6985cc50ee831b5b075589aa0ebb05 *ffmpeg-v30.1.2-win32-ia32.zip +b2fa79b739023651f0551ca06ac5da7b47acaaf8b09ccdedf7081fc3ea824a80 *ffmpeg-v30.1.2-win32-x64.zip +b2b562a45ae4a2d40bf039ed1c707a7875b9e893fc8c6a0044d536b0f9968629 *hunspell_dictionaries.zip +08da936356d1321eec550c30a9208750773d86e3be0b7fe4baf36c72a8609c20 *libcxx-objects-v30.1.2-linux-arm64.zip +8aa302ac17f6ac44f756cb9219f18d01d267d43f9af2dfd8d4626e9d01e584fa *libcxx-objects-v30.1.2-linux-armv7l.zip +1ab04d6cec407930a2051761e6114cb2cd6418e2103d32e835246d06c696b427 *libcxx-objects-v30.1.2-linux-x64.zip +4db17e017bdb818cca5d8e08a78fe54fb907c3cf05defd1b8f086b413075357a *libcxx_headers.zip +209f20bd3ee59fa7c85b0789d8e45168583407c9fb5bd2eba446c1e0796c4a7b *libcxxabi_headers.zip +4dcc59b7c66b9ccc881dbdfeba9de707f693fd839a8e764d34bb66dec7e3e63c *mksnapshot-v30.1.2-darwin-arm64.zip +582886552cbc227eb71f5047d00ac62baeb1912a52a8f5132953b94f54d41dd4 *mksnapshot-v30.1.2-darwin-x64.zip +639fde4957353d48be54b9075c0894b7529210f0bb3a2f9cad81ba2deb415080 *mksnapshot-v30.1.2-linux-arm64-x64.zip +1fec2ae49a9f03117a5e5b637866e5aa270da2fa3a007d0314c8a39ed392230f *mksnapshot-v30.1.2-linux-armv7l-x64.zip +494f86a178a2b14101c6deccf7c2ae88c690c7ce8445b63854a2e0525d69aaa0 *mksnapshot-v30.1.2-linux-x64.zip +bfbb90138b4df3f57fd9fe9cc05b6b8f9b3bf099b66d65215cc9434e2272b0d1 *mksnapshot-v30.1.2-mas-arm64.zip +d1a6a825628a141fdd73cf208180cc20af32b8e06aeb7f4d36566358cef40a90 *mksnapshot-v30.1.2-mas-x64.zip +fd523788a380990d589f9461f29a8878b63090146aa124f2debf3197af69929b *mksnapshot-v30.1.2-win32-arm64-x64.zip +69068d132cd511e33be9aff6dda5df74079c2003657cd89107d4eaaac7e4d997 *mksnapshot-v30.1.2-win32-ia32.zip +69bfab7461f83a256c0869e2781cca4f5a53f8b6a60d78617b79b8bfc0b554b6 *mksnapshot-v30.1.2-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index bcc9340406d..877d8afe243 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,7 +1,7 @@ -e0065c61f340e85106a99c4b54746c5cee09d59b08c5712f67f99e92aa44995d node-v20.11.1-darwin-arm64.tar.gz -c52e7fb0709dbe63a4cbe08ac8af3479188692937a7bd8e776e0eedfa33bb848 node-v20.11.1-darwin-x64.tar.gz -e34ab2fc2726b4abd896bcbff0250e9b2da737cbd9d24267518a802ed0606f3b node-v20.11.1-linux-arm64.tar.gz -e42791f76ece283c7a4b97fbf716da72c5128c54a9779f10f03ae74a4bcfb8f6 node-v20.11.1-linux-armv7l.tar.gz -bf3a779bef19452da90fb88358ec2c57e0d2f882839b20dc6afc297b6aafc0d7 node-v20.11.1-linux-x64.tar.gz -a5a9d30a8f7d56e00ccb27c1a7d24c8d0bc96a2689ebba8eb7527698793496f1 win-arm64/node.exe -bc585910690318aaebe3c57669cb83ca9d1e5791efd63195e238f54686e6c2ec win-x64/node.exe +d2148d79e9ff04d2982d00faeae942ceba488ca327a91065e528235167b9e9d6 node-v20.14.0-darwin-arm64.tar.gz +1dcc18a199cb5f46d43ed1c3c61b87a247d1a1a11dd6b32a36a9c46ac1088f86 node-v20.14.0-darwin-x64.tar.gz +d63e83fca4f81801396620c46a42892a2ef26e21a4508f68de373e61a12bd9c5 node-v20.14.0-linux-arm64.tar.gz +af45ea0d09e55a4f05c0190636532bdf9f70b2eaf0a1c4d7594207cf21284df0 node-v20.14.0-linux-armv7l.tar.gz +5b9bf40cfc7c21de14a1b4c367650e3c96eb101156bf9368ffc2f947414b6581 node-v20.14.0-linux-x64.tar.gz +a6ec02119098cf92592539e06289953c4365be20ab15d4ad264669f931000b0c win-arm64/node.exe +8f45741ec6ba07be8d199c0cebc838a58c0430c9228dbe50f8ac5d4859e58bae win-x64/node.exe diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index 85d27273861..a3daf1878b0 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const fs = require("fs"); +const minimatch = require("minimatch"); const vscode_universal_bundler_1 = require("vscode-universal-bundler"); const cross_spawn_promise_1 = require("@malept/cross-spawn-promise"); const root = path.dirname(path.dirname(__dirname)); @@ -18,25 +19,29 @@ async function main(buildDir) { const appName = product.nameLong + '.app'; const x64AppPath = path.join(buildDir, 'VSCode-darwin-x64', appName); const arm64AppPath = path.join(buildDir, 'VSCode-darwin-arm64', appName); - const x64AsarPath = path.join(x64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); - const arm64AsarPath = path.join(arm64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); + const asarRelativePath = path.join('Contents', 'Resources', 'app', 'node_modules.asar'); const outAppPath = path.join(buildDir, `VSCode-darwin-${arch}`, appName); const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json'); + const filesToSkip = [ + '**/CodeResources', + '**/Credits.rtf', + ]; await (0, vscode_universal_bundler_1.makeUniversalApp)({ x64AppPath, arm64AppPath, - x64AsarPath, - arm64AsarPath, - filesToSkip: [ - 'Credits.rtf', - 'CodeResources', - 'fsevents.node', - 'Info.plist', // TODO@deepak1556: regressed with 11.4.2 internal builds - 'MainMenu.nib', // Generated sequence is not deterministic with Xcode 13 - '.npmrc' - ], + asarPath: asarRelativePath, outAppPath, - force: true + force: true, + mergeASARs: true, + x64ArchFiles: '*/kerberos.node', + filesToSkipComparison: (file) => { + for (const expected of filesToSkip) { + if (minimatch(file, expected)) { + return true; + } + } + return false; + } }); const productJson = JSON.parse(fs.readFileSync(productJsonPath, 'utf8')); Object.assign(productJson, { diff --git a/build/darwin/create-universal-app.ts b/build/darwin/create-universal-app.ts index 04eb3a11e20..94b8a23b9e5 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -5,6 +5,7 @@ import * as path from 'path'; import * as fs from 'fs'; +import * as minimatch from 'minimatch'; import { makeUniversalApp } from 'vscode-universal-bundler'; import { spawn } from '@malept/cross-spawn-promise'; @@ -21,26 +22,31 @@ async function main(buildDir?: string) { const appName = product.nameLong + '.app'; const x64AppPath = path.join(buildDir, 'VSCode-darwin-x64', appName); const arm64AppPath = path.join(buildDir, 'VSCode-darwin-arm64', appName); - const x64AsarPath = path.join(x64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); - const arm64AsarPath = path.join(arm64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); + const asarRelativePath = path.join('Contents', 'Resources', 'app', 'node_modules.asar'); const outAppPath = path.join(buildDir, `VSCode-darwin-${arch}`, appName); const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json'); + const filesToSkip = [ + '**/CodeResources', + '**/Credits.rtf', + ]; + await makeUniversalApp({ x64AppPath, arm64AppPath, - x64AsarPath, - arm64AsarPath, - filesToSkip: [ - 'Credits.rtf', - 'CodeResources', - 'fsevents.node', - 'Info.plist', // TODO@deepak1556: regressed with 11.4.2 internal builds - 'MainMenu.nib', // Generated sequence is not deterministic with Xcode 13 - '.npmrc' - ], + asarPath: asarRelativePath, outAppPath, - force: true + force: true, + mergeASARs: true, + x64ArchFiles: '*/kerberos.node', + filesToSkipComparison: (file: string) => { + for (const expected of filesToSkip) { + if (minimatch(file, expected)) { + return true; + } + } + return false; + } }); const productJson = JSON.parse(fs.readFileSync(productJsonPath, 'utf8')); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index d7a814b9a1b..7d588611473 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -127,21 +127,7 @@ function getNodeVersion() { return { nodeVersion, internalNodeVersion }; } -function getNodeChecksum(nodeVersion, platform, arch, glibcPrefix) { - let expectedName; - switch (platform) { - case 'win32': - expectedName = product.nodejsRepository !== 'https://nodejs.org' ? - `win-${arch}-node.exe` : `win-${arch}/node.exe`; - break; - - case 'darwin': - case 'alpine': - case 'linux': - expectedName = `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`; - break; - } - +function getNodeChecksum(expectedName) { const nodeJsChecksums = fs.readFileSync(path.join(REPO_ROOT, 'build', 'checksums', 'nodejs.txt'), 'utf8'); for (const line of nodeJsChecksums.split('\n')) { const [checksum, name] = line.split(/\s+/); @@ -196,7 +182,24 @@ function nodejs(platform, arch) { log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejsRepository}...`); const glibcPrefix = process.env['VSCODE_NODE_GLIBC'] ?? ''; - const checksumSha256 = getNodeChecksum(nodeVersion, platform, arch, glibcPrefix); + let expectedName; + switch (platform) { + case 'win32': + expectedName = product.nodejsRepository !== 'https://nodejs.org' ? + `win-${arch}-node.exe` : `win-${arch}/node.exe`; + break; + + case 'darwin': + expectedName = `node-v${nodeVersion}-${platform}-${arch}.tar.gz`; + break; + case 'linux': + expectedName = `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`; + break; + case 'alpine': + expectedName = `node-v${nodeVersion}-linux-${arch}-musl.tar.gz`; + break; + } + const checksumSha256 = getNodeChecksum(expectedName); if (checksumSha256) { log(`Using SHA256 checksum for checking integrity: ${checksumSha256}`); @@ -207,13 +210,13 @@ function nodejs(platform, arch) { switch (platform) { case 'win32': return (product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `win-${arch}-node.exe`, checksumSha256 }) : + fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) : fetchUrls(`/dist/v${nodeVersion}/win-${arch}/node.exe`, { base: 'https://nodejs.org', checksumSha256 })) .pipe(rename('node.exe')); case 'darwin': case 'linux': return (product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`, checksumSha256 }) : + fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) : fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 }) ).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) .pipe(filter('**/node')) @@ -221,7 +224,7 @@ function nodejs(platform, arch) { .pipe(rename('node')); case 'alpine': return product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}-${platform}-${arch}.tar.gz`, checksumSha256 }) + fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) .pipe(filter('**/node')) .pipe(util.setExecutableBit('**')) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index b3b35466af0..4af406751f9 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -292,6 +292,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op '**/node-pty/lib/shared/conout.js', '**/*.wasm', '**/@vscode/vsce-sign/bin/*', + ], [ + '**/*.mk', ], 'node_modules.asar')); let all = es.merge( diff --git a/build/lib/asar.js b/build/lib/asar.js index 31845f2f2dd..07b39bf79ff 100644 --- a/build/lib/asar.js +++ b/build/lib/asar.js @@ -11,7 +11,7 @@ const pickle = require('chromium-pickle-js'); const Filesystem = require('asar/lib/filesystem'); const VinylFile = require("vinyl"); const minimatch = require("minimatch"); -function createAsar(folderPath, unpackGlobs, destFilename) { +function createAsar(folderPath, unpackGlobs, skipGlobs, destFilename) { const shouldUnpackFile = (file) => { for (let i = 0; i < unpackGlobs.length; i++) { if (minimatch(file.relative, unpackGlobs[i])) { @@ -20,6 +20,14 @@ function createAsar(folderPath, unpackGlobs, destFilename) { } return false; }; + const shouldSkipFile = (file) => { + for (const skipGlob of skipGlobs) { + if (minimatch(file.relative, skipGlob)) { + return true; + } + } + return false; + }; const filesystem = new Filesystem(folderPath); const out = []; // Keep track of pending inserts @@ -64,6 +72,9 @@ function createAsar(folderPath, unpackGlobs, destFilename) { if (!file.stat.isFile()) { throw new Error(`unknown item in stream!`); } + if (shouldSkipFile(file)) { + return; + } const shouldUnpack = shouldUnpackFile(file); insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack); if (shouldUnpack) { diff --git a/build/lib/asar.ts b/build/lib/asar.ts index 44a6416bdfb..7dc1dd3b2e6 100644 --- a/build/lib/asar.ts +++ b/build/lib/asar.ts @@ -17,7 +17,7 @@ declare class AsarFilesystem { insertFile(path: string, shouldUnpack: boolean, file: { stat: { size: number; mode: number } }, options: {}): Promise; } -export function createAsar(folderPath: string, unpackGlobs: string[], destFilename: string): NodeJS.ReadWriteStream { +export function createAsar(folderPath: string, unpackGlobs: string[], skipGlobs: string[], destFilename: string): NodeJS.ReadWriteStream { const shouldUnpackFile = (file: VinylFile): boolean => { for (let i = 0; i < unpackGlobs.length; i++) { @@ -28,6 +28,15 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena return false; }; + const shouldSkipFile = (file: VinylFile): boolean => { + for (const skipGlob of skipGlobs) { + if (minimatch(file.relative, skipGlob)) { + return true; + } + } + return false; + }; + const filesystem = new Filesystem(folderPath); const out: Buffer[] = []; @@ -78,6 +87,9 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena if (!file.stat.isFile()) { throw new Error(`unknown item in stream!`); } + if (shouldSkipFile(file)) { + return; + } const shouldUnpack = shouldUnpackFile(file); insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack); diff --git a/build/linux/debian/dep-lists.js b/build/linux/debian/dep-lists.js index d843c090063..3a642a72725 100644 --- a/build/linux/debian/dep-lists.js +++ b/build/linux/debian/dep-lists.js @@ -31,6 +31,7 @@ exports.referenceGeneratedDepsByArch = { 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.2.5)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', @@ -67,6 +68,7 @@ exports.referenceGeneratedDepsByArch = { 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libc6 (>= 2.4)', 'libc6 (>= 2.9)', @@ -108,6 +110,7 @@ exports.referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts index 4028370cd02..86d1de12216 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts @@ -31,6 +31,7 @@ export const referenceGeneratedDepsByArch = { 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.2.5)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', @@ -67,6 +68,7 @@ export const referenceGeneratedDepsByArch = { 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libc6 (>= 2.4)', 'libc6 (>= 2.9)', @@ -108,6 +110,7 @@ export const referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index bff0c9a25df..19adbeb0529 100644 --- a/build/linux/dependencies-generator.js +++ b/build/linux/dependencies-generator.js @@ -23,7 +23,7 @@ const product = require("../../product.json"); // The reference dependencies, which one has to update when the new dependencies // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/dependencies-generator.ts b/build/linux/dependencies-generator.ts index 226310e1258..5fe4ac5da64 100644 --- a/build/linux/dependencies-generator.ts +++ b/build/linux/dependencies-generator.ts @@ -25,7 +25,7 @@ import product = require('../../product.json'); // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index 8be477290bb..97984514511 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -45,12 +45,14 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', 'libc.so.6(GLIBC_2.3.3)(64bit)', 'libc.so.6(GLIBC_2.3.4)(64bit)', 'libc.so.6(GLIBC_2.4)(64bit)', + 'libc.so.6(GLIBC_2.5)(64bit)', 'libc.so.6(GLIBC_2.6)(64bit)', 'libc.so.6(GLIBC_2.7)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)', @@ -140,8 +142,10 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.16)', 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', + 'libc.so.6(GLIBC_2.25)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', + 'libc.so.6(GLIBC_2.5)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', 'libc.so.6(GLIBC_2.8)', @@ -241,6 +245,7 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6()(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 24b18d504c8..b79812784bf 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -44,12 +44,14 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', 'libc.so.6(GLIBC_2.3.3)(64bit)', 'libc.so.6(GLIBC_2.3.4)(64bit)', 'libc.so.6(GLIBC_2.4)(64bit)', + 'libc.so.6(GLIBC_2.5)(64bit)', 'libc.so.6(GLIBC_2.6)(64bit)', 'libc.so.6(GLIBC_2.7)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)', @@ -139,8 +141,10 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.16)', 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', + 'libc.so.6(GLIBC_2.25)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', + 'libc.so.6(GLIBC_2.5)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', 'libc.so.6(GLIBC_2.8)', @@ -240,6 +244,7 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6()(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', diff --git a/build/package.json b/build/package.json index 0bbeed3f136..9bff15d4691 100644 --- a/build/package.json +++ b/build/package.json @@ -51,7 +51,7 @@ "ternary-stream": "^3.0.0", "through2": "^4.0.2", "tmp": "^0.2.1", - "vscode-universal-bundler": "^0.0.2", + "vscode-universal-bundler": "^0.1.0", "workerpool": "^6.4.0", "yauzl": "^2.10.0" }, diff --git a/build/yarn.lock b/build/yarn.lock index d99ceffaadf..74200931c51 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -199,6 +199,15 @@ events "^3.0.0" tslib "^2.2.0" +"@electron/asar@^3.2.7": + version "3.2.10" + resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.10.tgz#615cf346b734b23cafa4e0603551010bd0e50aa8" + integrity sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw== + dependencies: + commander "^5.0.0" + glob "^7.1.6" + minimatch "^3.0.4" + "@electron/get@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960" @@ -329,10 +338,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.0.tgz#2efddf82828aac85e64cef62482af61c29561bee" integrity sha512-NgJnesu1RtWihtTtXGFMU5YSE6JyyHPMxCwBZK7a6/8d31GuSo9l0Ss7w1Jw5QnKUawG6UEehs883kcXf5fYwg== -"@malept/cross-spawn-promise@^1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" - integrity sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ== +"@malept/cross-spawn-promise@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz#d0772de1aa680a0bfb9ba2f32b4c828c7857cb9d" + integrity sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg== dependencies: cross-spawn "^7.0.1" @@ -686,6 +695,11 @@ optionalDependencies: keytar "^7.7.0" +"@xmldom/xmldom@^0.8.8": + version "0.8.10" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -755,18 +769,6 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -asar@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b" - integrity sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw== - dependencies: - chromium-pickle-js "^0.2.0" - commander "^5.0.0" - glob "^7.1.6" - minimatch "^3.0.4" - optionalDependencies: - "@types/glob" "^7.1.1" - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -787,11 +789,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - azure-devops-node-api@^11.0.1: version "11.2.0" resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz#bf04edbef60313117a0507415eed4790a420ad6b" @@ -847,6 +844,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -877,11 +881,6 @@ buffer-equal-constant-time@1.0.1: resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= -buffer-equal@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= - buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" @@ -995,11 +994,6 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chromium-pickle-js@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" - integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= - clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -1048,11 +1042,6 @@ color-support@^1.1.3: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -colors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" - integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= - colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -1065,13 +1054,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" - integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= - dependencies: - graceful-readlink ">= 1.0.0" - commander@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" @@ -1185,15 +1167,13 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -dir-compare@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-2.4.0.tgz#785c41dc5f645b34343a4eafc50b79bac7f11631" - integrity sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA== +dir-compare@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-4.2.0.tgz#d1d4999c14fbf55281071fdae4293b3b9ce86f19" + integrity sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ== dependencies: - buffer-equal "1.0.0" - colors "1.0.3" - commander "2.9.0" - minimatch "3.0.4" + minimatch "^3.0.5" + p-limit "^3.1.0 " dom-serializer@^2.0.0: version "2.0.0" @@ -1413,6 +1393,15 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@^11.1.1: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -1422,16 +1411,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1550,11 +1529,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= - gulp-merge-json@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/gulp-merge-json/-/gulp-merge-json-2.1.1.tgz#cfb1d066467577545b8c1c289a278e6ef4b4e0de" @@ -1926,19 +1900,26 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@^3.0.3, minimatch@^3.0.5, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.3, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +minimatch@^9.0.3: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" minimist@^1.2.0, minimist@^1.2.3: version "1.2.6" @@ -2062,6 +2043,13 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +"p-limit@^3.1.0 ": + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + parse-node-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" @@ -2127,6 +2115,15 @@ plist@^3.0.1: base64-js "^1.5.1" xmlbuilder "^9.0.7" +plist@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" + integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== + dependencies: + "@xmldom/xmldom" "^0.8.8" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + plugin-error@1.0.1, plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" @@ -2678,16 +2675,18 @@ vscode-gulp-watch@^5.0.3: vinyl "^2.2.0" vinyl-file "^3.0.0" -vscode-universal-bundler@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/vscode-universal-bundler/-/vscode-universal-bundler-0.0.2.tgz#2c988dac681d3ffe6baec6defac0995cb833c55a" - integrity sha512-FPJcvKnQGBqFzy6M6Nm2yvAczNLUeXsfYM6GwCex/pUOkvIM2icIHmiSvtMJINlLW1iG+oEwE3/LVbABmcjEmQ== +vscode-universal-bundler@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/vscode-universal-bundler/-/vscode-universal-bundler-0.1.0.tgz#1a03d1d16c6ea5065318fafbc2a554b7c2f3bd32" + integrity sha512-wtT9IZ/fqIZSirY6cxElu8a6WpNaOjgQjjazt85lMCWBuF/tWVw5nRYX67pTVsUyi6kzQaIvfyyIvxVbBpetBA== dependencies: - "@malept/cross-spawn-promise" "^1.1.0" - asar "^3.0.3" + "@electron/asar" "^3.2.7" + "@malept/cross-spawn-promise" "^2.0.0" debug "^4.3.1" - dir-compare "^2.4.0" - fs-extra "^9.0.1" + dir-compare "^4.2.0" + fs-extra "^11.1.1" + minimatch "^9.0.3" + plist "^3.1.0" webidl-conversions@^3.0.0: version "3.0.1" @@ -2727,6 +2726,11 @@ xml2js@^0.4.19, xml2js@^0.4.23: sax ">=0.6.0" xmlbuilder "~11.0.0" +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + xmlbuilder@^9.0.7: version "9.0.7" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" @@ -2756,3 +2760,8 @@ yazl@^2.2.2: integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== dependencies: buffer-crc32 "~0.2.3" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/cgmanifest.json b/cgmanifest.json index 61747342eef..aa06f8323b0 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "f1a45d7ded05d64ca8136cc142ddc0c271b1dd43" + "commitHash": "7fa4f6e14e0707c0e604cf7c1da33566e78169ce" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "122.0.6261.156" + "version": "124.0.6367.243" }, { "component": { @@ -48,7 +48,7 @@ "git": { "name": "ffmpeg", "repositoryUrl": "https://chromium.googlesource.com/chromium/third_party/ffmpeg", - "commitHash": "17525de887d54b970ffdd421a0879c1db1952307" + "commitHash": "52d8ef3799b2f16b66351dd0972bb0bcee1648ac" } }, "isOnlyProductionDependency": true, @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "9b1bf44ea9e7785e38c93b7d22d32dbca262df6c" + "commitHash": "fe0f08a5dd68fd72b1652adaa51ab07a4b09f847" } }, "isOnlyProductionDependency": true, - "version": "20.11.1" + "version": "20.14.0" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "f9ed0eaee4b172733872c2f84e5061882dd08e5c" + "commitHash": "91de7d0f13208891c5604e00ccd18e4f6826653b" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "29.4.0" + "version": "30.1.2" }, { "component": { diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index 8e12e622e05..2fab3ec306a 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -164,8 +164,8 @@ export function activate(context: vscode.ExtensionContext) { const serverCommandPath = path.join(vscodePath, 'scripts', serverCommand); outputChannel.appendLine(`Launching server: "${serverCommandPath}" ${commandArgs.join(' ')}`); - - extHostProcess = cp.spawn(serverCommandPath, commandArgs, { env, cwd: vscodePath }); + const shell = (process.platform === 'win32'); + extHostProcess = cp.spawn(serverCommandPath, commandArgs, { env, cwd: vscodePath, shell }); } else { const extensionToInstall = process.env['TESTRESOLVER_INSTALL_BUILTIN_EXTENSION']; if (extensionToInstall) { @@ -182,8 +182,8 @@ export function activate(context: vscode.ExtensionContext) { outputChannel.appendLine(`Using server build at ${serverLocation}`); outputChannel.appendLine(`Server arguments ${commandArgs.join(' ')}`); - - extHostProcess = cp.spawn(path.join(serverLocation, 'bin', serverCommand), commandArgs, { env, cwd: serverLocation }); + const shell = (process.platform === 'win32'); + extHostProcess = cp.spawn(path.join(serverLocation, 'bin', serverCommand), commandArgs, { env, cwd: serverLocation, shell }); } extHostProcess.stdout!.on('data', (data: Buffer) => processOutput(data.toString())); extHostProcess.stderr!.on('data', (data: Buffer) => processOutput(data.toString())); diff --git a/package.json b/package.json index b99927cdeef..cc00db38c76 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.92.0", - "distro": "6e11724ed97152dcd4510f2520755c983a6f439c", + "distro": "abf8b974fc98ae45b7729d258b0459182c617887", "author": { "name": "Microsoft Corporation" }, @@ -93,7 +93,7 @@ "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.3", - "kerberos": "^2.0.1", + "kerberos": "2.1.1-alpha.0", "minimist": "^1.2.6", "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", @@ -136,7 +136,7 @@ "@vscode/l10n-dev": "0.0.35", "@vscode/telemetry-extractor": "^1.10.2", "@vscode/test-cli": "^0.0.6", - "@vscode/test-electron": "^2.3.8", + "@vscode/test-electron": "^2.4.0", "@vscode/test-web": "^0.0.56", "@vscode/v8-heap-parser": "^0.1.0", "@vscode/vscode-perf": "^0.0.14", @@ -149,7 +149,7 @@ "cssnano": "^6.0.3", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "29.4.0", + "electron": "30.1.2", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^46.5.0", diff --git a/remote/.yarnrc b/remote/.yarnrc index 4c99388e889..8d07643c18e 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,5 +1,5 @@ disturl "https://nodejs.org/dist" -target "20.11.1" -ms_build_id "275039" +target "20.14.0" +ms_build_id "282653" runtime "node" build_from_source "true" diff --git a/remote/package.json b/remote/package.json index bb615b88ea4..c4558964774 100644 --- a/remote/package.json +++ b/remote/package.json @@ -26,7 +26,7 @@ "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.3", - "kerberos": "^2.0.1", + "kerberos": "2.1.1-alpha.0", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta11", diff --git a/remote/yarn.lock b/remote/yarn.lock index 0f65b688c17..7ca95a92629 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -388,14 +388,14 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -kerberos@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.0.1.tgz#663b0b46883b4da84495f60f2e9e399a43a33ef5" - integrity sha512-O/jIgbdGK566eUhFwIcgalbqirYU/r76MW7/UFw06Fd9x5bSwgyZWL/Vm26aAmezQww/G9KYkmmJBkEkPk5HLw== +kerberos@2.1.1-alpha.0: + version "2.1.1-alpha.0" + resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.1.1-alpha.0.tgz#c6d377b43c8206340fd184167754f2c81dad5ab1" + integrity sha512-II8N/ky/Vpd8y7LTxwCuAYoQ8XeV3HYxuK7IDmyoFacIhDljx4sdt/+sOwqgXEweQyCHlbZSKSaK82upqNM4Hw== dependencies: bindings "^1.5.0" - node-addon-api "^4.3.0" - prebuild-install "7.1.1" + node-addon-api "^6.1.0" + prebuild-install "^7.1.2" lru-cache@^6.0.0: version "6.0.0" @@ -469,10 +469,10 @@ node-addon-api@^3.2.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-addon-api@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== node-gyp-build@4.8.1, node-gyp-build@^4.3.0: version "4.8.1" @@ -503,10 +503,10 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -prebuild-install@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" - integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== +prebuild-install@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== dependencies: detect-libc "^2.0.0" expand-template "^2.0.3" diff --git a/src/main.js b/src/main.js index be7cd98afaf..7f909f7d633 100644 --- a/src/main.js +++ b/src/main.js @@ -298,10 +298,8 @@ function configureCommandlineSwitchesSync(cliArgs) { app.commandLine.appendSwitch('disable-features', featuresToDisable); // Blink features to configure. - // `FontMatchingCTMigration` - Siwtch font matching on macOS to CoreText (Refs https://github.com/microsoft/vscode/issues/214390). - // TODO(deepak1556): Enable this feature again after updating to Electron 30. const blinkFeaturesToDisable = - `FontMatchingCTMigration,${app.commandLine.getSwitchValue('disable-blink-features')}`; + `${app.commandLine.getSwitchValue('disable-blink-features')}`; app.commandLine.appendSwitch('disable-blink-features', blinkFeaturesToDisable); // Support JS Flags diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index 7223c49a13b..0a98250767b 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -72,10 +72,11 @@ async function launchServer(options: LaunchOptions) { logger.log(`Storing log files into '${serverLogsPath}'`); logger.log(`Command line: '${serverLocation}' ${args.join(' ')}`); + const shell: boolean = (process.platform === 'win32'); const serverProcess = spawn( serverLocation, args, - { env } + { env, shell } ); logger.log(`Started server for browser smoke tests (pid: ${serverProcess.pid})`); diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 990b7cd19fd..2613f10da62 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -192,11 +192,11 @@ async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.U serverArgs.push('--logsPath', serverLogsPath); const stdio: cp.StdioOptions = args.debug ? 'pipe' : ['ignore', 'pipe', 'ignore']; - + const shell: boolean = (process.platform === 'win32'); const serverProcess = cp.spawn( serverLocation, serverArgs, - { env, stdio } + { env, stdio, shell } ); if (args.debug) { diff --git a/yarn.lock b/yarn.lock index 17a69acb6fb..f31bd180a3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1029,11 +1029,6 @@ dependencies: defer-to-connect "^2.0.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-3.0.0.tgz#d52238c9052d746c9689523e650160e70786bc9a" @@ -1639,15 +1634,16 @@ supports-color "^9.4.0" yargs "^17.7.2" -"@vscode/test-electron@^2.3.8": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.3.8.tgz#06a7c50b38cfac0ede833905e088d55c61cd12d3" - integrity sha512-b4aZZsBKtMGdDljAsOPObnAi7+VWIaYl3ylCz1jTs+oV6BZ4TNHcVNC3xUn0azPeszBmwSBDQYfFESIaUQnrOg== +"@vscode/test-electron@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.4.0.tgz#6fcdbac10948960c15f8970cf5d5e624dd51a524" + integrity sha512-yojuDFEjohx6Jb+x949JRNtSn6Wk2FAh4MldLE3ck9cfvCqzwxF32QsNy1T9Oe4oT+ZfFcg0uPUCajJzOmPlTA== dependencies: - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.4" jszip "^3.10.1" - semver "^7.5.2" + ora "^7.0.1" + semver "^7.6.2" "@vscode/test-web@^0.0.56": version "0.0.56" @@ -2086,13 +2082,6 @@ acorn@^8.8.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -agent-base@6: - version "6.0.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" - integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== - dependencies: - debug "4" - agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" @@ -2605,6 +2594,15 @@ bl@^4.0.2, bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +bl@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" + integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" @@ -2736,6 +2734,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" @@ -2906,6 +2912,11 @@ chalk@^4.x: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.0, chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -3002,6 +3013,18 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-spinners@^2.9.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -3894,15 +3917,20 @@ electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.717.tgz#99db370cae8cd090d5b01f8748e9ad369924d0f8" integrity sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A== -electron@29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-29.4.0.tgz#5dcd5a977414337a2518619e9166c0e86a5a3bae" - integrity sha512-4DTO8U66oiI8rShrDSu2zDPW6GWRiCebyb1MHSfQkLWCNI/PnLyGKeqYPUoVgc0FWaNN2sCBn8NKJHb++hE2LQ== +electron@30.1.2: + version "30.1.2" + resolved "https://registry.yarnpkg.com/electron/-/electron-30.1.2.tgz#9c8b9b0d0e3f07783d8c5dbd9519b3ffd11f1551" + integrity sha512-A5CFGwbA+HSXnzwjc8fP2GIezBcAb0uN/VbNGLOW8DHOYn07rvJ/1bAJECHUUzt5zbfohveG3hpMQiYpbktuDw== dependencies: "@electron/get" "^2.0.0" "@types/node" "^20.9.0" extract-zip "^2.0.1" +emoji-regex@^10.2.1: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -5588,15 +5616,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz#e9096c5afd071a3fce56e6252bb321583c124673" @@ -5621,14 +5640,6 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - https-proxy-agent@^7.0.0: version "7.0.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" @@ -5680,7 +5691,7 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -6010,6 +6021,11 @@ is-gzip@^1.0.0: resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + is-negated-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" @@ -6114,6 +6130,11 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unicode-supported@^1.1.0, is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -6423,14 +6444,14 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== -kerberos@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.0.1.tgz#663b0b46883b4da84495f60f2e9e399a43a33ef5" - integrity sha512-O/jIgbdGK566eUhFwIcgalbqirYU/r76MW7/UFw06Fd9x5bSwgyZWL/Vm26aAmezQww/G9KYkmmJBkEkPk5HLw== +kerberos@2.1.1-alpha.0: + version "2.1.1-alpha.0" + resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.1.1-alpha.0.tgz#c6d377b43c8206340fd184167754f2c81dad5ab1" + integrity sha512-II8N/ky/Vpd8y7LTxwCuAYoQ8XeV3HYxuK7IDmyoFacIhDljx4sdt/+sOwqgXEweQyCHlbZSKSaK82upqNM4Hw== dependencies: bindings "^1.5.0" - node-addon-api "^4.3.0" - prebuild-install "7.1.1" + node-addon-api "^6.1.0" + prebuild-install "^7.1.2" keygrip@~1.1.0: version "1.1.0" @@ -6749,6 +6770,14 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-symbols@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" + integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== + dependencies: + chalk "^5.0.0" + is-unicode-supported "^1.1.0" + lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" @@ -7056,7 +7085,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -7388,16 +7417,16 @@ node-addon-api@^4.2.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== -node-addon-api@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== - node-addon-api@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.0.0.tgz#cfb3574e6df708ff71a30db6c4762d9e06e11c27" integrity sha512-GyHvgPvUXBvAkXa0YvYnhilSB1A+FRYMpIVggKzPZqdaZfevZOuzfWzyvgzOwRLHBeo/MMswmJFsrNF4Nw1pmA== +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + node-fetch@2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" @@ -7640,6 +7669,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + only@~0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" @@ -7685,6 +7721,21 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-7.0.1.tgz#cdd530ecd865fe39e451a0e7697865669cb11930" + integrity sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw== + dependencies: + chalk "^5.3.0" + cli-cursor "^4.0.0" + cli-spinners "^2.9.0" + is-interactive "^2.0.0" + is-unicode-supported "^1.3.0" + log-symbols "^5.1.0" + stdin-discarder "^0.1.0" + string-width "^6.1.0" + strip-ansi "^7.1.0" + ordered-read-streams@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" @@ -8410,10 +8461,10 @@ postcss@^8.4.33: picocolors "^1.0.0" source-map-js "^1.2.0" -prebuild-install@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" - integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== +prebuild-install@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== dependencies: detect-libc "^2.0.0" expand-template "^2.0.3" @@ -8879,6 +8930,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -9030,7 +9089,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.4: +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -9044,6 +9103,11 @@ semver@^7.5.3: dependencies: lru-cache "^6.0.0" +semver@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + serialize-error@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" @@ -9422,6 +9486,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +stdin-discarder@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21" + integrity sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ== + dependencies: + bl "^5.0.0" + stream-combiner@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" @@ -9546,6 +9617,15 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string-width@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-6.1.0.tgz#96488d6ed23f9ad5d82d13522af9e4c4c3fd7518" + integrity sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^10.2.1" + strip-ansi "^7.0.1" + string.prototype.padend@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.1.tgz#824c84265dbac46cade2b957b38b6a5d8d1683c5" @@ -9625,7 +9705,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: +strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== From 16654e61265ca89e53612bb7a61f89f4e99aeb3c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 9 Jul 2024 08:08:56 +0200 Subject: [PATCH 014/798] esm - reduce diff to branch (#221154) --- scripts/code-server.js | 1 - src/bootstrap-amd.js | 152 ++++++++++---- src/bootstrap-fork.js | 15 +- src/bootstrap-meta.js | 16 +- src/bootstrap-node.js | 74 ++++--- src/bootstrap-window.js | 188 +++++++++++++----- src/bootstrap.js | 24 ++- src/cli.js | 29 ++- src/main.js | 42 ++-- src/server-cli.js | 20 +- src/server-main.js | 46 +++-- src/tsec.exemptions.json | 3 + src/typings/vscode-globals-product.d.ts | 11 + src/vs/base/common/jsonc.js | 12 +- src/vs/base/common/performance.js | 9 + src/vs/base/common/semver/semver.d.ts | 7 +- src/vs/base/common/semver/semver.js | 45 +++++ src/vs/base/node/nls.js | 14 ++ src/vs/base/node/unc.js | 12 ++ .../base/parts/sandbox/common/sandboxTypes.ts | 5 + src/vs/base/parts/storage/node/storage.ts | 8 +- .../processExplorer/processExplorer.js | 3 +- .../electron-sandbox/workbench/workbench.js | 3 +- .../platform/environment/node/userDataPath.js | 16 +- .../node/nativeModules.integrationTest.ts | 5 + src/vs/platform/request/node/proxy.ts | 20 +- src/vs/platform/terminal/node/ptyService.ts | 9 +- src/vs/server/node/server.cli.ts | 39 ++-- .../api/node/extHostExtensionService.ts | 6 +- src/vs/workbench/api/node/proxyResolver.ts | 11 + .../issue/electron-sandbox/issueReporter.js | 3 +- 31 files changed, 650 insertions(+), 198 deletions(-) diff --git a/scripts/code-server.js b/scripts/code-server.js index c043bf2671b..56945e76ca7 100644 --- a/scripts/code-server.js +++ b/scripts/code-server.js @@ -69,4 +69,3 @@ function startServer(programArgs) { } main(); - diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 27d15eb76d5..f8a0c00be95 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -7,10 +7,28 @@ 'use strict'; /** - * @typedef {import('./vs/nls').INLSConfiguration} INLSConfiguration + * @import { INLSConfiguration } from './vs/nls' * @import { IProductConfiguration } from './vs/base/common/product' */ +// ESM-comment-begin +const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// import * as path from 'path'; +// import * as fs from 'fs'; +// import { fileURLToPath } from 'url'; +// import { createRequire } from 'node:module'; +// import { product, pkg } from './bootstrap-meta.js'; +// import * as bootstrap from './bootstrap.js'; +// import * as performance from './vs/base/common/performance.js'; +// +// const require = createRequire(import.meta.url); +// const isESM = true; +// const module = { exports: {} }; +// const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// ESM-uncomment-end + // Store the node.js require function in a variable // before loading our AMD loader to avoid issues // when this file is bundled with other files. @@ -21,7 +39,12 @@ globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target // VSCODE_GLOBALS: package/product.json /** @type Partial */ +// ESM-comment-begin globalThis._VSCODE_PRODUCT_JSON = require('./bootstrap-meta').product; +// ESM-comment-end +// ESM-uncomment-begin +// globalThis._VSCODE_PRODUCT_JSON = { ...product }; +// ESM-uncomment-end if (process.env['VSCODE_DEV']) { // Patch product overrides when running out of sources try { @@ -30,29 +53,21 @@ if (process.env['VSCODE_DEV']) { globalThis._VSCODE_PRODUCT_JSON = Object.assign(globalThis._VSCODE_PRODUCT_JSON, overrides); } catch (error) { /* ignore */ } } +// ESM-comment-begin globalThis._VSCODE_PACKAGE_JSON = require('./bootstrap-meta').pkg; +// ESM-comment-end +// ESM-uncomment-begin +// globalThis._VSCODE_PACKAGE_JSON = { ...pkg }; +// ESM-uncomment-end -// @ts-ignore -const loader = require('./vs/loader'); +// VSCODE_GLOBALS: file root of all resources +globalThis._VSCODE_FILE_ROOT = __dirname; + +// ESM-comment-begin const bootstrap = require('./bootstrap'); -const performance = require('./vs/base/common/performance'); +const performance = require(`./vs/base/common/performance`); const fs = require('fs'); - -// Bootstrap: Loader -loader.config({ - baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }), - catchError: true, - nodeRequire, - amdModulesPattern: /^vs\//, - recordStats: true -}); - -// Running in Electron -if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { - loader.define('fs', ['original-fs'], function (/** @type {import('fs')} */originalFS) { - return originalFS; // replace the patched electron fs with the original node fs for all AMD code - }); -} +// ESM-comment-end //#region NLS helpers @@ -138,31 +153,82 @@ async function doSetupNLS() { //#endregion -/** - * @param {string=} entrypoint - * @param {(value: any) => void=} onLoad - * @param {(err: Error) => void=} onError - */ -exports.load = function (entrypoint, onLoad, onError) { - if (!entrypoint) { - return; - } +//#region Loader Config - // code cache config - if (process.env['VSCODE_CODE_CACHE_PATH']) { - loader.config({ - nodeCachedData: { - path: process.env['VSCODE_CODE_CACHE_PATH'], - seed: entrypoint - } +if (isESM) { + + /** + * @param {string=} entrypoint + * @param {(value: any) => void} [onLoad] + * @param {(err: Error) => void} [onError] + */ + module.exports.load = function (entrypoint, onLoad, onError) { + if (!entrypoint) { + return; + } + + entrypoint = `./${entrypoint}.js`; + + onLoad = onLoad || function () { }; + onError = onError || function (err) { console.error(err); }; + + setupNLS().then(() => { + performance.mark(`code/fork/willLoadCode`); + import(entrypoint).then(onLoad, onError); + }); + }; +} else { + + // @ts-ignore + const loader = require('./vs/loader'); + + loader.config({ + baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }), + catchError: true, + nodeRequire, + amdModulesPattern: /^vs\//, + recordStats: true + }); + + // Running in Electron + if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { + loader.define('fs', ['original-fs'], function (/** @type {import('fs')} */originalFS) { + return originalFS; // replace the patched electron fs with the original node fs for all AMD code }); } - onLoad = onLoad || function () { }; - onError = onError || function (err) { console.error(err); }; + /** + * @param {string=} entrypoint + * @param {(value: any) => void} [onLoad] + * @param {(err: Error) => void} [onError] + */ + module.exports.load = function (entrypoint, onLoad, onError) { + if (!entrypoint) { + return; + } - setupNLS().then(() => { - performance.mark('code/fork/willLoadCode'); - loader([entrypoint], onLoad, onError); - }); -}; + // code cache config + if (process.env['VSCODE_CODE_CACHE_PATH']) { + loader.config({ + nodeCachedData: { + path: process.env['VSCODE_CODE_CACHE_PATH'], + seed: entrypoint + } + }); + } + + onLoad = onLoad || function () { }; + onError = onError || function (err) { console.error(err); }; + + setupNLS().then(() => { + performance.mark('code/fork/willLoadCode'); + loader([entrypoint], onLoad, onError); + }); + }; +} + +//#endregion + +// ESM-uncomment-begin +// export const load = module.exports.load; +// ESM-uncomment-end diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 9de1e6f0d15..a95cbf53589 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -6,11 +6,20 @@ //@ts-check 'use strict'; +// ESM-comment-begin const performance = require('./vs/base/common/performance'); -performance.mark('code/fork/start'); - const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); +const bootstrapAmd = require('./bootstrap-amd'); +// ESM-comment-end +// ESM-uncomment-begin +// import * as performance from './vs/base/common/performance.js'; +// import * as bootstrap from './bootstrap.js'; +// import * as bootstrapNode from './bootstrap-node.js'; +// import * as bootstrapAmd from './bootstrap-amd.js'; +// ESM-uncomment-end + +performance.mark('code/fork/start'); // Crash reporter configureCrashReporter(); @@ -41,7 +50,7 @@ if (process.env['VSCODE_PARENT_PID']) { } // Load AMD entry point -require('./bootstrap-amd').load(process.env['VSCODE_AMD_ENTRYPOINT']); +bootstrapAmd.load(process.env['VSCODE_AMD_ENTRYPOINT']); //#region Helpers diff --git a/src/bootstrap-meta.js b/src/bootstrap-meta.js index 7924b77eec8..46041d5d91a 100644 --- a/src/bootstrap-meta.js +++ b/src/bootstrap-meta.js @@ -10,6 +10,13 @@ * @import { IProductConfiguration } from './vs/base/common/product' */ +// ESM-uncomment-begin +// import { createRequire } from 'node:module'; +// +// const require = createRequire(import.meta.url); +// const module = { exports: {} }; +// ESM-uncomment-end + /** @type Partial & { BUILD_INSERT_PRODUCT_CONFIGURATION?: string } */ let product = { BUILD_INSERT_PRODUCT_CONFIGURATION: 'BUILD_INSERT_PRODUCT_CONFIGURATION' }; // DO NOT MODIFY, PATCHED DURING BUILD if (product['BUILD_INSERT_PRODUCT_CONFIGURATION']) { @@ -24,5 +31,10 @@ if (pkg['BUILD_INSERT_PACKAGE_CONFIGURATION']) { pkg = require('../package.json'); // Running out of sources } -exports.product = product; -exports.pkg = pkg; +module.exports.product = product; +module.exports.pkg = pkg; + +// ESM-uncomment-begin +// export const product = module.exports.product; +// export const pkg = module.exports.pkg; +// ESM-uncomment-end diff --git a/src/bootstrap-node.js b/src/bootstrap-node.js index 914b8290380..4a829557f0d 100644 --- a/src/bootstrap-node.js +++ b/src/bootstrap-node.js @@ -6,12 +6,28 @@ //@ts-check 'use strict'; +// ESM-comment-begin +const path = require('path'); +const fs = require('fs'); + +const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// import * as path from 'path'; +// import * as fs from 'fs'; +// import { fileURLToPath } from 'url'; +// import { createRequire } from 'node:module'; +// +// const require = createRequire(import.meta.url); +// const isESM = true; +// const module = { exports: {} }; +// const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// ESM-uncomment-end + // Setup current working directory in all our node & electron processes // - Windows: call `process.chdir()` to always set application folder as cwd // - all OS: store the `process.cwd()` inside `VSCODE_CWD` for consistent lookups function setupCurrentWorkingDirectory() { - const path = require('path'); - try { // Store the `process.cwd()` inside `VSCODE_CWD` @@ -38,36 +54,41 @@ setupCurrentWorkingDirectory(); * * @param {string} injectPath */ -exports.injectNodeModuleLookupPath = function (injectPath) { +module.exports.injectNodeModuleLookupPath = function (injectPath) { if (!injectPath) { throw new Error('Missing injectPath'); } - const Module = require('module'); - const path = require('path'); + const Module = require('node:module'); + if (isESM) { + // register a loader hook + // ESM-uncomment-begin + // Module.register('./server-loader.mjs', { parentURL: import.meta.url, data: injectPath }); + // ESM-uncomment-end + } else { + const nodeModulesPath = path.join(__dirname, '../node_modules'); - const nodeModulesPath = path.join(__dirname, '../node_modules'); + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; - // @ts-ignore - const originalResolveLookupPaths = Module._resolveLookupPaths; - - // @ts-ignore - Module._resolveLookupPaths = function (moduleName, parent) { - const paths = originalResolveLookupPaths(moduleName, parent); - if (Array.isArray(paths)) { - for (let i = 0, len = paths.length; i < len; i++) { - if (paths[i] === nodeModulesPath) { - paths.splice(i, 0, injectPath); - break; + // @ts-ignore + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); + if (Array.isArray(paths)) { + for (let i = 0, len = paths.length; i < len; i++) { + if (paths[i] === nodeModulesPath) { + paths.splice(i, 0, injectPath); + break; + } } } - } - return paths; - }; + return paths; + }; + } }; -exports.removeGlobalNodeModuleLookupPaths = function () { +module.exports.removeGlobalNodeModuleLookupPaths = function () { const Module = require('module'); // @ts-ignore const globalPaths = Module.globalPaths; @@ -95,10 +116,7 @@ exports.removeGlobalNodeModuleLookupPaths = function () { * @param {Partial} product * @returns {{ portableDataPath: string; isPortable: boolean; }} */ -exports.configurePortable = function (product) { - const fs = require('fs'); - const path = require('path'); - +module.exports.configurePortable = function (product) { const appRoot = path.dirname(__dirname); /** @@ -158,3 +176,9 @@ exports.configurePortable = function (product) { isPortable }; }; + +// ESM-uncomment-begin +// export const injectNodeModuleLookupPath = module.exports.injectNodeModuleLookupPath; +// export const removeGlobalNodeModuleLookupPaths = module.exports.removeGlobalNodeModuleLookupPaths; +// export const configurePortable = module.exports.configurePortable; +// ESM-uncomment-end diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index cd859a847f9..59ddc3fdfbf 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -10,12 +10,20 @@ /** * @import { ISandboxConfiguration } from './vs/base/parts/sandbox/common/sandboxTypes' + * @typedef {any} LoaderConfig */ -/* eslint-disable no-restricted-globals */ +/* eslint-disable no-restricted-globals, */ + +// ESM-comment-begin +const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// const isESM = true; +// ESM-uncomment-end // Simple module style to support node.js and browser environments -(function (globalThis, factory) { +(function (factory) { // Node.js if (typeof exports === 'object') { @@ -27,7 +35,7 @@ // @ts-ignore globalThis.MonacoBootstrapWindow = factory(); } -}(this, function () { +}(function () { const bootstrapLib = bootstrap(); const preloadGlobals = sandboxGlobals(); const safeProcess = preloadGlobals.process; @@ -96,59 +104,137 @@ window['MonacoEnvironment'] = {}; - /** @type {any} */ - const loaderConfig = { - baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out`, - preferScriptTags: true - }; + if (isESM) { - // use a trusted types policy when loading via script tags - loaderConfig.trustedTypesPolicy = window.trustedTypes?.createPolicy('amdLoader', { - createScriptURL(value) { - if (value.startsWith(window.location.origin)) { - return value; - } - throw new Error(`Invalid script url: ${value}`); + // Signal before require() + if (typeof options?.beforeRequire === 'function') { + options.beforeRequire(configuration); } - }); - // Teach the loader the location of the node modules we use in renderers - // This will enable to load these modules via - + diff --git a/src/vs/code/electron-sandbox/workbench/workbench-dev.html b/src/vs/code/electron-sandbox/workbench/workbench-dev.html index a92bea242a4..b1be2b7527c 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench-dev.html +++ b/src/vs/code/electron-sandbox/workbench/workbench-dev.html @@ -68,8 +68,7 @@ - - + diff --git a/src/vs/nls.ts b/src/vs/nls.ts index 5a546325fc7..8303d8a32e2 100644 --- a/src/vs/nls.ts +++ b/src/vs/nls.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// VSCODE_GLOBALS: NLS const isPseudo = globalThis._VSCODE_NLS_LANGUAGE === 'pseudo' || (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); export interface ILocalizeInfo { @@ -87,7 +86,6 @@ export function localize(data: ILocalizeInfo | string /* | number when built */, * depending on the target context. */ function lookupMessage(index: number, fallback: string | null): string { - // VSCODE_GLOBALS: NLS const message = globalThis._VSCODE_NLS_MESSAGES?.[index]; if (typeof message !== 'string') { if (typeof fallback === 'string') { diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts index d2997c2d22b..fb0033b2ada 100644 --- a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts +++ b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts @@ -118,21 +118,18 @@ flakySuite('Native Modules (all platforms)', () => { assert.ok(typeof result === 'string' || typeof result === 'undefined', testErrorMessage('@vscode/windows-registry')); }); - test('@vscode/windows-ca-certs', async () => { - // @ts-ignore we do not directly depend on this module anymore - // but indirectly from our dependency to `@vscode/proxy-agent` - // we still want to ensure this module can work properly. - const windowsCerts = await import('@vscode/windows-ca-certs'); - const store = new windowsCerts.Crypt32(); - assert.ok(windowsCerts, testErrorMessage('@vscode/windows-ca-certs')); - let certCount = 0; - try { - while (store.next()) { - certCount++; + test('@vscode/proxy-agent', async () => { + const proxyAgent = await import('@vscode/proxy-agent'); + // This call will load `@vscode/proxy-agent` which is a native module that we want to test on Windows + const windowsCerts = await proxyAgent.loadSystemCertificates({ + log: { + trace: () => { }, + debug: () => { }, + info: () => { }, + warn: () => { }, + error: () => { } } - } finally { - store.done(); - } - assert(certCount > 0); + }); + assert.ok(windowsCerts.length > 0, testErrorMessage('@vscode/proxy-agent')); }); }); diff --git a/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts b/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts index cedabb0c126..8d1c7fb8679 100644 --- a/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts +++ b/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule } from 'vs/amdX'; import { getErrorMessage } from 'vs/base/common/errors'; import { IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { TargetPlatform } from 'vs/platform/extensions/common/extensions'; @@ -95,14 +96,7 @@ export class ExtensionSignatureVerificationService implements IExtensionSignatur private vsceSign(): Promise { if (!this.moduleLoadingPromise) { - this.moduleLoadingPromise = new Promise( - (resolve, reject) => require( - ['@vscode/vsce-sign'], - async (obj) => { - const instance = obj; - - return resolve(instance); - }, reject)); + this.moduleLoadingPromise = importAMDNodeModule('@vscode/vsce-sign', 'src/main.js'); } return this.moduleLoadingPromise; diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 2b42d690c91..1aea4ead9b9 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -83,7 +83,6 @@ export class IssueMainService implements IIssueMainService { }, product, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE } diff --git a/src/vs/platform/issue/electron-main/processMainService.ts b/src/vs/platform/issue/electron-main/processMainService.ts index 76da45d897c..e6002ae0192 100644 --- a/src/vs/platform/issue/electron-main/processMainService.ts +++ b/src/vs/platform/issue/electron-main/processMainService.ts @@ -155,7 +155,6 @@ export class ProcessMainService implements IProcessMainService { data, product, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE } diff --git a/src/vs/platform/sign/browser/signService.ts b/src/vs/platform/sign/browser/signService.ts index a9d699bf3b2..b6b08ebf466 100644 --- a/src/vs/platform/sign/browser/signService.ts +++ b/src/vs/platform/sign/browser/signService.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule, resolveAmdNodeModulePath } from 'vs/amdX'; import { WindowIntervalTimer } from 'vs/base/browser/dom'; import { mainWindow } from 'vs/base/browser/window'; +import { isESM } from 'vs/base/common/amd'; import { memoize } from 'vs/base/common/decorators'; import { FileAccess } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -62,7 +64,7 @@ export class SignService extends AbstractSignService implements ISignService { let [wasm] = await Promise.all([ this.getWasmBytes(), new Promise((resolve, reject) => { - require(['vsda'], resolve, reject); + importAMDNodeModule('vsda', 'rust/web/vsda.js').then(() => resolve(), reject); // todo@connor4312: there seems to be a bug(?) in vscode-loader with // require() not resolving in web once the script loads, so check manually @@ -74,7 +76,6 @@ export class SignService extends AbstractSignService implements ISignService { }).finally(() => checkInterval.dispose()), ]); - const keyBytes = new TextEncoder().encode(this.productService.serverLicense?.join('\n') || ''); for (let i = 0; i + STEP_SIZE < keyBytes.length; i += STEP_SIZE) { const key = await crypto.subtle.importKey('raw', keyBytes.slice(i + IV_SIZE, i + IV_SIZE + KEY_SIZE), { name: 'AES-CBC' }, false, ['decrypt']); @@ -87,7 +88,10 @@ export class SignService extends AbstractSignService implements ISignService { } private async getWasmBytes(): Promise { - const response = await fetch(FileAccess.asBrowserUri('vsda/../vsda_bg.wasm').toString(true)); + const url = isESM + ? resolveAmdNodeModulePath('vsda', 'rust/web/vsda_bg.wasm') + : FileAccess.asBrowserUri('vsda/../vsda_bg.wasm').toString(true); + const response = await fetch(url); if (!response.ok) { throw new Error('error loading vsda'); } diff --git a/src/vs/platform/sign/common/abstractSignService.ts b/src/vs/platform/sign/common/abstractSignService.ts index 6f7c91ba958..838dde91518 100644 --- a/src/vs/platform/sign/common/abstractSignService.ts +++ b/src/vs/platform/sign/common/abstractSignService.ts @@ -36,7 +36,7 @@ export abstract class AbstractSignService implements ISignService { }; } } catch (e) { - // ignore errors silently + console.error(e); } return { id: '', data: value }; } @@ -54,7 +54,7 @@ export abstract class AbstractSignService implements ISignService { try { return (validator.validate(value) === 'ok'); } catch (e) { - // ignore errors silently + console.error(e); return false; } finally { validator.dispose?.(); @@ -65,7 +65,7 @@ export abstract class AbstractSignService implements ISignService { try { return await this.signValue(value); } catch (e) { - // ignore errors silently + console.error(e); } return value; } diff --git a/src/vs/platform/sign/node/signService.ts b/src/vs/platform/sign/node/signService.ts index d07ba9cfbe9..1a2023d6ad1 100644 --- a/src/vs/platform/sign/node/signService.ts +++ b/src/vs/platform/sign/node/signService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule } from 'vs/amdX'; import { AbstractSignService, IVsdaValidator } from 'vs/platform/sign/common/abstractSignService'; import { ISignService } from 'vs/platform/sign/common/sign'; @@ -29,6 +30,6 @@ export class SignService extends AbstractSignService implements ISignService { } private vsda(): Promise { - return new Promise((resolve, reject) => require(['vsda'], resolve, reject)); + return importAMDNodeModule('vsda', 'index.js'); } } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 070c7ae05d3..5e6279486fc 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1445,7 +1445,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic userEnv: { ...this.initialUserEnv, ...options.userEnv }, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE }, diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 29aa95e5683..d9bb0122f64 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -696,7 +696,7 @@ export async function createServer(address: string | net.AddressInfo | null, arg let didLogAboutSIGPIPE = false; process.on('SIGPIPE', () => { // See https://github.com/microsoft/vscode-remote-release/issues/6543 - // We would normally install a SIGPIPE listener in bootstrap.js + // We would normally install a SIGPIPE listener in bootstrap-node.js // But in certain situations, the console itself can be in a broken pipe state // so logging SIGPIPE to the console will cause an infinite async loop if (!didLogAboutSIGPIPE) { diff --git a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts index fe3632fb8d0..1bd9688ea69 100644 --- a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts @@ -23,6 +23,7 @@ import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRe import { NullState } from 'vs/editor/common/languages/nullTokenize'; import { MetadataConsts, StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ITextModel } from 'vs/editor/common/model'; +import { FileAccess } from 'vs/base/common/network'; function getIRange(range: IRange): IRange { return { @@ -56,7 +57,7 @@ function registerLanguageConfiguration(instantiationService: TestInstantiationSe let configPath: string; switch (languageId) { case LanguageId.TypeScript: - configPath = path.join('extensions', 'typescript-basics', 'language-configuration.json'); + configPath = FileAccess.asFileUri('vs/workbench/contrib/codeEditor/test/node/language-configuration.json').fsPath; break; default: throw new Error('Unknown languageId'); diff --git a/src/vs/workbench/contrib/codeEditor/test/node/language-configuration.json b/src/vs/workbench/contrib/codeEditor/test/node/language-configuration.json new file mode 100644 index 00000000000..25a23685738 --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/test/node/language-configuration.json @@ -0,0 +1,250 @@ +{ + // Note that this file should stay in sync with 'javascript-language-basics/javascript-language-configuration.json' + "comments": { + "lineComment": "//", + "blockComment": [ + "/*", + "*/" + ] + }, + "brackets": [ + [ + "${", + "}" + ], + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] + ], + "autoClosingPairs": [ + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "`", + "close": "`", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } + ], + "surroundingPairs": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "'", + "'" + ], + [ + "\"", + "\"" + ], + [ + "`", + "`" + ], + [ + "<", + ">" + ] + ], + "colorizedBracketPairs": [ + [ + "(", + ")" + ], + [ + "[", + "]" + ], + [ + "{", + "}" + ], + [ + "<", + ">" + ] + ], + "autoCloseBefore": ";:.,=}])>` \n\t", + "folding": { + "markers": { + "start": "^\\s*//\\s*#?region\\b", + "end": "^\\s*//\\s*#?endregion\\b" + } + }, + "wordPattern": { + "pattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\@\\~\\!\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>/\\?\\s]+)", + }, + "indentationRules": { + "decreaseIndentPattern": { + "pattern": "^\\s*[\\}\\]\\)].*$" + }, + "increaseIndentPattern": { + "pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$" + }, + // e.g. * ...| or */| or *-----*/| + "unIndentedLinePattern": { + "pattern": "^(\\t|[ ])*[ ]\\*[^/]*\\*/\\s*$|^(\\t|[ ])*[ ]\\*/\\s*$|^(\\t|[ ])*\\*([ ]([^\\*]|\\*(?!/))*)?$" + }, + "indentNextLinePattern": { + "pattern": "^((.*=>\\s*)|((.*[^\\w]+|\\s*)(if|while|for)\\s*\\(.*\\)\\s*))$" + } + }, + "onEnterRules": [ + { + // e.g. /** | */ + "beforeText": { + "pattern": "^\\s*/\\*\\*(?!/)([^\\*]|\\*(?!/))*$" + }, + "afterText": { + "pattern": "^\\s*\\*/$" + }, + "action": { + "indent": "indentOutdent", + "appendText": " * " + } + }, + { + // e.g. /** ...| + "beforeText": { + "pattern": "^\\s*/\\*\\*(?!/)([^\\*]|\\*(?!/))*$" + }, + "action": { + "indent": "none", + "appendText": " * " + } + }, + { + // e.g. * ...| + "beforeText": { + "pattern": "^(\\t|[ ])*\\*([ ]([^\\*]|\\*(?!/))*)?$" + }, + "previousLineText": { + "pattern": "(?=^(\\s*(/\\*\\*|\\*)).*)(?=(?!(\\s*\\*/)))" + }, + "action": { + "indent": "none", + "appendText": "* " + } + }, + { + // e.g. */| + "beforeText": { + "pattern": "^(\\t|[ ])*[ ]\\*/\\s*$" + }, + "action": { + "indent": "none", + "removeText": 1 + }, + }, + { + // e.g. *-----*/| + "beforeText": { + "pattern": "^(\\t|[ ])*[ ]\\*[^/]*\\*/\\s*$" + }, + "action": { + "indent": "none", + "removeText": 1 + }, + }, + { + "beforeText": { + "pattern": "^\\s*(\\bcase\\s.+:|\\bdefault:)$" + }, + "afterText": { + "pattern": "^(?!\\s*(\\bcase\\b|\\bdefault\\b))" + }, + "action": { + "indent": "indent" + } + }, + { + // Decrease indentation after single line if/else if/else, for, or while + "previousLineText": "^\\s*(((else ?)?if|for|while)\\s*\\(.*\\)\\s*|else\\s*)$", + // But make sure line doesn't have braces or is not another if statement + "beforeText": "^\\s+([^{i\\s]|i(?!f\\b))", + "action": { + "indent": "outdent" + } + }, + // Indent when pressing enter from inside () + { + "beforeText": "^.*\\([^\\)]*$", + "afterText": "^\\s*\\).*$", + "action": { + "indent": "indentOutdent", + "appendText": "\t", + } + }, + // Indent when pressing enter from inside {} + { + "beforeText": "^.*\\{[^\\}]*$", + "afterText": "^\\s*\\}.*$", + "action": { + "indent": "indentOutdent", + "appendText": "\t", + } + }, + // Indent when pressing enter from inside [] + { + "beforeText": "^.*\\[[^\\]]*$", + "afterText": "^\\s*\\].*$", + "action": { + "indent": "indentOutdent", + "appendText": "\t", + } + }, + ] +} diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html index 455e823692a..9d853e358a0 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html @@ -39,8 +39,7 @@ - - + diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html index c6290004d2d..8d0bcc6c87c 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html @@ -39,5 +39,5 @@ - + diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 8f4dc4a6b57..ca86cea6dbc 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -6,6 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { parentOriginHash } from 'vs/base/browser/iframe'; import { mainWindow } from 'vs/base/browser/window'; +import { isESM } from 'vs/base/common/amd'; import { Barrier } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { canceled, onUnexpectedError } from 'vs/base/common/errors'; @@ -183,14 +184,14 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost } if (event.data.type === 'vscode.bootstrap.nls') { const factoryModuleId = 'vs/base/worker/workerMain.js'; - const baseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); + const baseUrl = isESM ? undefined : require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); iframe.contentWindow!.postMessage({ type: event.data.type, data: { baseUrl, - workerUrl: require.toUrl(factoryModuleId), + workerUrl: isESM ? FileAccess.asBrowserUri(factoryModuleId).toString(true) : require.toUrl(factoryModuleId), + fileRoot: globalThis._VSCODE_FILE_ROOT, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE } diff --git a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html index 3983bdb9dd4..0ad94fc8b30 100644 --- a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html @@ -4,7 +4,7 @@ @@ -78,7 +78,7 @@ return; } const { data } = event.data; - createWorker(data.baseUrl, data.workerUrl, data.nls.messages, data.nls.language); + createWorker(data.baseUrl, data.workerUrl, data.fileRoot, data.nls.messages, data.nls.language); }; window.parent.postMessage({ @@ -87,24 +87,35 @@ }, '*'); } - function createWorker(baseUrl, workerUrl, nlsMessages, nlsLanguage) { + function createWorker(baseUrl, workerUrl, fileRoot, nlsMessages, nlsLanguage) { try { if (globalThis.crossOriginIsolated) { workerUrl += '?vscode-coi=2'; // COEP } + // ESM-comment-begin + const isESM = false; + // ESM-comment-end + // ESM-uncomment-begin + // const isESM = true; + // ESM-uncomment-end + const blob = new Blob([[ `/*extensionHostWorker*/`, `globalThis.MonacoEnvironment = { baseUrl: '${baseUrl}' };`, - // VSCODE_GLOBALS: NLS `globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(nlsMessages)};`, `globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(nlsLanguage)};`, - `importScripts('${workerUrl}');`, + `globalThis._VSCODE_FILE_ROOT = '${fileRoot}';`, + isESM ? `await import('${workerUrl}');` : `importScripts('${workerUrl}');`, + isESM ? `globalThis.onmessage({ data: 'vs/workbench/api/worker/extensionHostWorker' });` : undefined, // important to start loading after the ESM async import has finished `/*extensionHostWorker*/` ].join('')], { type: 'application/javascript' }); - const worker = new Worker(URL.createObjectURL(blob), { name }); - worker.postMessage('vs/workbench/api/worker/extensionHostWorker'); + const worker = new Worker(URL.createObjectURL(blob), { name, type: isESM ? 'module' : undefined }); + if (!isESM) { + // Note: cannot postMessage into a worker that is ESM because imports are async + worker.postMessage('vs/workbench/api/worker/extensionHostWorker'); + } const nestedWorkers = new Map(); worker.onmessage = (event) => { diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index b0a0f4ddb23..a924cdaa5e6 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -252,7 +252,6 @@ async function runTestsInBrowser(testModules, browserType) { // when running from `out-build`, ensure to load the default // messages file, because all `nls.localize` calls have their // english values removed and replaced by an index. - // VSCODE_GLOBALS: NLS // @ts-ignore globalThis._VSCODE_NLS_MESSAGES = JSON.parse(value); }, nlsMessages); diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index d4d85a18521..73fc7c65abe 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -66,7 +66,7 @@ const assert = require('assert'); const path = require('path'); const glob = require('glob'); const util = require('util'); -const bootstrap = require('../../../src/bootstrap'); +const bootstrapNode = require('../../../src/bootstrap-node'); const coverage = require('../coverage'); const { takeSnapshotAndCountClasses } = require('../analyzeSnapshot'); @@ -102,7 +102,6 @@ function initNls(opts) { // when running from `out-build`, ensure to load the default // messages file, because all `nls.localize` calls have their // english values removed and replaced by an index. - // VSCODE_GLOBALS: NLS globalThis._VSCODE_NLS_MESSAGES = (require.__$__nodeRequire ?? require)(`../../../out-build/nls.messages.json`); } } @@ -116,7 +115,7 @@ function initLoader(opts) { const loaderConfig = { nodeRequire: require, catchError: true, - baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../src'), { isWindows: process.platform === 'win32' }), + baseUrl: bootstrapNode.fileUriFromPath(path.join(__dirname, '../../../src'), { isWindows: process.platform === 'win32' }), paths: { 'vs': `../${outdir}/vs`, 'lib': `../${outdir}/lib`, diff --git a/test/unit/node/index.js b/test/unit/node/index.js index 9f0ec662a73..d087e8409f3 100644 --- a/test/unit/node/index.js +++ b/test/unit/node/index.js @@ -17,6 +17,7 @@ const minimatch = require('minimatch'); const coverage = require('../coverage'); const minimist = require('minimist'); const { takeSnapshotAndCountClasses } = require('../analyzeSnapshot'); +const bootstrapNode = require('../../../src/bootstrap-node'); /** * @type {{ build: boolean; run: string; runGlob: string; coverage: boolean; help: boolean; coverageFormats: string | string[]; coveragePath: string; }} @@ -88,7 +89,6 @@ function main() { // when running from `out-build`, ensure to load the default // messages file, because all `nls.localize` calls have their // english values removed and replaced by an index. - // VSCODE_GLOBALS: NLS globalThis._VSCODE_NLS_MESSAGES = require(`../../../${out}/nls.messages.json`); } @@ -106,41 +106,9 @@ function main() { console.error(e.stack || e); }); - /** - * @param {string} path - * @param {{ isWindows?: boolean, scheme?: string, fallbackAuthority?: string }} config - * @returns {string} - */ - function fileUriFromPath(path, config) { - - // Since we are building a URI, we normalize any backslash - // to slashes and we ensure that the path begins with a '/'. - let pathName = path.replace(/\\/g, '/'); - if (pathName.length > 0 && pathName.charAt(0) !== '/') { - pathName = `/${pathName}`; - } - - /** @type {string} */ - let uri; - - // Windows: in order to support UNC paths (which start with '//') - // that have their own authority, we do not use the provided authority - // but rather preserve it. - if (config.isWindows && pathName.startsWith('//')) { - uri = encodeURI(`${config.scheme || 'file'}:${pathName}`); - } - - // Otherwise we optionally add the provided authority if specified - else { - uri = encodeURI(`${config.scheme || 'file'}://${config.fallbackAuthority || ''}${pathName}`); - } - - return uri.replace(/#/g, '%23'); - } - const loaderConfig = { nodeRequire: require, - baseUrl: fileUriFromPath(src, { isWindows: process.platform === 'win32' }), + baseUrl: bootstrapNode.fileUriFromPath(src, { isWindows: process.platform === 'win32' }), catchError: true }; From 1456e644b0baae937548d16de536c5581fcc4b9e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 6 Aug 2024 07:34:30 -0700 Subject: [PATCH 673/798] Fix tests --- .../contrib/terminal/browser/terminalProcessManager.ts | 4 ++-- src/vs/workbench/contrib/terminal/common/terminal.ts | 4 ++-- .../contrib/terminal/test/browser/terminalInstance.test.ts | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index ca6b79173b4..ff6ff01ea8e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -293,7 +293,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce nonce: this.shellIntegrationNonce }, windowsEnableConpty: this._terminalConfigurationService.config.windowsEnableConpty, - windowsUseConptyDll: this._terminalConfigurationService.config.experimental.windowsUseConptyDll, + windowsUseConptyDll: this._terminalConfigurationService.config.experimental?.windowsUseConptyDll ?? false, environmentVariableCollections: this._extEnvironmentVariableCollection?.collections ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined, workspaceFolder: this._cwdWorkspaceFolder, }; @@ -494,7 +494,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce nonce: this.shellIntegrationNonce }, windowsEnableConpty: this._terminalConfigurationService.config.windowsEnableConpty, - windowsUseConptyDll: this._terminalConfigurationService.config.experimental.windowsUseConptyDll, + windowsUseConptyDll: this._terminalConfigurationService.config.experimental?.windowsUseConptyDll ?? false, environmentVariableCollections: this._extEnvironmentVariableCollection ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined, workspaceFolder: this._cwdWorkspaceFolder, }; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index d7b1856b0d5..2bbbfd79329 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -214,8 +214,8 @@ export interface ITerminalConfiguration { smoothScrolling: boolean; ignoreBracketedPasteMode: boolean; rescaleOverlappingGlyphs: boolean; - experimental: { - windowsUseConptyDll: boolean; + experimental?: { + windowsUseConptyDll?: boolean; }; } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts index 4a5579e21c7..58a23373a57 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts @@ -141,9 +141,6 @@ suite('Workbench - TerminalInstance', () => { unicodeVersion: '6', shellIntegration: { enabled: true - }, - experimental: { - windowsUseConptyDll: false } } }, From 2075322aaf6ac7eb2430262288724d524c295c0f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 6 Aug 2024 17:26:38 +0200 Subject: [PATCH 674/798] Update yaml grammar (#224954) Part of #224862 --- extensions/yaml/cgmanifest.json | 4 ++-- extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json | 5 +++-- extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json | 5 +++-- extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json | 6 ++++-- extensions/yaml/syntaxes/yaml.tmLanguage.json | 13 ++++++++++++- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/extensions/yaml/cgmanifest.json b/extensions/yaml/cgmanifest.json index c75a4e52f89..18882ead40a 100644 --- a/extensions/yaml/cgmanifest.json +++ b/extensions/yaml/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "RedCMD/YAML-Syntax-Highlighter", "repositoryUrl": "https://github.com/RedCMD/YAML-Syntax-Highlighter", - "commitHash": "60e2e6e24c63d5a703cb04577678a2e416edd956" + "commitHash": "d4dca9f38a654ebbb13c1b72b7881e3c5864a778" } }, "licenseDetail": [ @@ -21,7 +21,7 @@ "THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ], "license": "MIT", - "version": "1.0.1" + "version": "1.1.1" } ], "version": 1 diff --git a/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json index 6ca3d9bf1b8..7ae77112860 100644 --- a/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/60e2e6e24c63d5a703cb04577678a2e416edd956", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/dfd7e5f4f71f9695c5d8697ca57f81240165aa04", "name": "YAML 1.0", "scopeName": "source.yaml.1.0", "comment": "https://yaml.org/spec/1.0/", @@ -500,7 +500,8 @@ }, "block-map-value": { "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", - "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])", + "//": "Assumming 3rd party preprocessing variables `{{...}}` turn into valid map-keys when inside a block-mapping", + "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])|(?<=}})(?=[\t ]++#|[\t ]*+$)", "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029}]]|-[^\\x{85 2028 2029}\r\n\t ])", "beginCaptures": { "0": { diff --git a/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json index 3b7974a1a5d..57a96ac1e4f 100644 --- a/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/60e2e6e24c63d5a703cb04577678a2e416edd956", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/dfd7e5f4f71f9695c5d8697ca57f81240165aa04", "name": "YAML 1.1", "scopeName": "source.yaml.1.1", "comment": "https://yaml.org/spec/1.1/", @@ -641,7 +641,8 @@ }, "block-map-value": { "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", - "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])", + "//": "Assumming 3rd party preprocessing variables `{{...}}` turn into valid map-keys when inside a block-mapping", + "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])|(?<=}})(?=[\t ]++#|[\t ]*+$)", "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029}]]|-[^\\x{85 2028 2029}\r\n\t ])", "beginCaptures": { "0": { diff --git a/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json index 711f5c8e422..965b6040816 100644 --- a/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/60e2e6e24c63d5a703cb04577678a2e416edd956", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/d4dca9f38a654ebbb13c1b72b7881e3c5864a778", "name": "YAML 1.2", "scopeName": "source.yaml.1.2", "comment": "https://yaml.org/spec/1.2.2", @@ -37,6 +37,7 @@ ] }, { + "comment": "For when YAML is embedded inside a Markdown code-block", "begin": "\\G", "while": "\\G", "name": "meta.stream.yaml", @@ -633,7 +634,8 @@ }, "block-map-value": { "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", - "begin": ":(?=[\r\n\t ])", + "//": "Assumming 3rd party preprocessing variables `{{...}}` turn into valid map-keys when inside a block-mapping", + "begin": ":(?=[\r\n\t ])|(?<=}})(?=[\t ]++#|[\t ]*+$)", "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{FEFF}]]|-[^\r\n\t ])", "beginCaptures": { "0": { diff --git a/extensions/yaml/syntaxes/yaml.tmLanguage.json b/extensions/yaml/syntaxes/yaml.tmLanguage.json index 8be76d7a3c5..3a64fbd850e 100644 --- a/extensions/yaml/syntaxes/yaml.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml.tmLanguage.json @@ -4,10 +4,21 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/274009903e20ac6dc37ba5763fb853744e28c9b2", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/d4dca9f38a654ebbb13c1b72b7881e3c5864a778", "name": "YAML Ain't Markup Language", "scopeName": "source.yaml", "patterns": [ + { + "comment": "Support legacy FrontMatter integration", + "//": "https://github.com/microsoft/vscode-markdown-tm-grammar/pull/162", + "begin": "(?<=^-{3,}\\s*+)\\G$", + "while": "^(?! {3,0}-{3,}[ \t]*+$|[ \t]*+\\.{3}$)", + "patterns": [ + { + "include": "source.yaml.1.2" + } + ] + }, { "comment": "Default to YAML version 1.2", "include": "source.yaml.1.2" From 382422232790348e067b527d84f0f292700f97fb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 6 Aug 2024 18:04:01 +0200 Subject: [PATCH 675/798] Fixing hover disappearance in editors that have `overflowingWidgetDomNode` set (#224845) * adding code * adding review changes * removing the unecessary target check --- .../hover/browser/contentHoverController.ts | 19 +++++++++++++++++- .../contrib/hover/browser/hoverController.ts | 20 +++++++++---------- .../contrib/hover/browser/hoverUtils.ts | 17 ++++++++++++++++ .../hover/browser/marginHoverWidget.ts | 13 +++++++++++- 4 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 src/vs/editor/contrib/hover/browser/hoverUtils.ts diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index d79984f24c1..4664ec67af3 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -20,6 +20,7 @@ import { ContentHoverComputer } from 'vs/editor/contrib/hover/browser/contentHov import { HoverResult } from 'vs/editor/contrib/hover/browser/contentHoverTypes'; import { Emitter } from 'vs/base/common/event'; import { RenderedContentHover } from 'vs/editor/contrib/hover/browser/contentHoverRendered'; +import { isMousePositionWithinElement } from 'vs/editor/contrib/hover/browser/hoverUtils'; export class ContentHoverController extends Disposable implements IHoverWidget { @@ -69,11 +70,15 @@ export class ContentHoverController extends Disposable implements IHoverWidget { const messages = (result.hasLoadingMessage ? this._addLoadingMessage(result.value) : result.value); this._withResult(new HoverResult(this._computer.anchor, messages, result.isComplete)); })); - this._register(dom.addStandardDisposableListener(this._contentHoverWidget.getDomNode(), 'keydown', (e) => { + const contentHoverWidgetNode = this._contentHoverWidget.getDomNode(); + this._register(dom.addStandardDisposableListener(contentHoverWidgetNode, 'keydown', (e) => { if (e.equals(KeyCode.Escape)) { this.hide(); } })); + this._register(dom.addStandardDisposableListener(contentHoverWidgetNode, 'mouseleave', (e) => { + this._onMouseLeave(e); + })); this._register(TokenizationRegistry.onDidChange(() => { if (this._contentHoverWidget.position && this._currentResult) { this._setCurrentResult(this._currentResult); // render again @@ -281,6 +286,14 @@ export class ContentHoverController extends Disposable implements IHoverWidget { return anchorCandidates; } + private _onMouseLeave(e: MouseEvent): void { + const editorDomNode = this._editor.getDomNode(); + const isMousePositionOutsideOfEditor = !editorDomNode || !isMousePositionWithinElement(editorDomNode, e.x, e.y); + if (isMousePositionOutsideOfEditor) { + this.hide(); + } + } + public startShowingAtRange(range: Range, mode: HoverStartMode, source: HoverStartSource, focus: boolean): void { this._startShowingOrUpdateHover(new HoverRangeAnchor(0, range, undefined, undefined), mode, source, focus, null); } @@ -363,6 +376,10 @@ export class ContentHoverController extends Disposable implements IHoverWidget { this._setCurrentResult(null); } + public getDomNode(): HTMLElement { + return this._contentHoverWidget.getDomNode(); + } + public get isColorPickerVisible(): boolean { return this._renderedContentHover?.isColorPickerVisible() ?? false; } diff --git a/src/vs/editor/contrib/hover/browser/hoverController.ts b/src/vs/editor/contrib/hover/browser/hoverController.ts index b6ef34860ca..2d4a15463ba 100644 --- a/src/vs/editor/contrib/hover/browser/hoverController.ts +++ b/src/vs/editor/contrib/hover/browser/hoverController.ts @@ -7,7 +7,7 @@ import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution, IScrollEvent } from 'vs/editor/common/editorCommon'; @@ -19,7 +19,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver'; import { HoverVerbosityAction } from 'vs/editor/common/languages'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { ContentHoverWidget } from 'vs/editor/contrib/hover/browser/contentHoverWidget'; +import { isMousePositionWithinElement } from 'vs/editor/contrib/hover/browser/hoverUtils'; import { ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHoverController'; import 'vs/css!./hover'; import { MarginHoverWidget } from 'vs/editor/contrib/hover/browser/marginHoverWidget'; @@ -160,19 +160,19 @@ export class HoverController extends Disposable implements IEditorContribution { } private _isMouseOnMarginHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { - const target = mouseEvent.target; - if (!target) { - return false; + const marginHoverWidgetNode = this._glyphWidget?.getDomNode(); + if (marginHoverWidgetNode) { + return isMousePositionWithinElement(marginHoverWidgetNode, mouseEvent.event.posx, mouseEvent.event.posy); } - return target.type === MouseTargetType.OVERLAY_WIDGET && target.detail === MarginHoverWidget.ID; + return false; } private _isMouseOnContentHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { - const target = mouseEvent.target; - if (!target) { - return false; + const contentWidgetNode = this._contentWidget?.getDomNode(); + if (contentWidgetNode) { + return isMousePositionWithinElement(contentWidgetNode, mouseEvent.event.posx, mouseEvent.event.posy); } - return target.type === MouseTargetType.CONTENT_WIDGET && target.detail === ContentHoverWidget.ID; + return false; } private _onEditorMouseUp(): void { diff --git a/src/vs/editor/contrib/hover/browser/hoverUtils.ts b/src/vs/editor/contrib/hover/browser/hoverUtils.ts new file mode 100644 index 00000000000..3f9ab067c1d --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/hoverUtils.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/browser/dom'; + +export function isMousePositionWithinElement(element: HTMLElement, posx: number, posy: number): boolean { + const elementRect = dom.getDomNodePagePosition(element); + if (posx < elementRect.left + || posx > elementRect.left + elementRect.width + || posy < elementRect.top + || posy > elementRect.top + elementRect.height) { + return false; + } + return true; +} diff --git a/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts b/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts index fa9577f32dc..e6fb9f4ce60 100644 --- a/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts @@ -14,6 +14,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget'; import { IHoverWidget } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { IHoverMessage, LaneOrLineNumber, MarginHoverComputer } from 'vs/editor/contrib/hover/browser/marginHoverComputer'; +import { isMousePositionWithinElement } from 'vs/editor/contrib/hover/browser/hoverUtils'; const $ = dom.$; @@ -59,7 +60,9 @@ export class MarginHoverWidget extends Disposable implements IOverlayWidget, IHo this._updateFont(); } })); - + this._register(dom.addStandardDisposableListener(this._hover.containerDomNode, 'mouseleave', (e) => { + this._onMouseLeave(e); + })); this._editor.addOverlayWidget(this); } @@ -181,4 +184,12 @@ export class MarginHoverWidget extends Disposable implements IOverlayWidget, IHo this._hover.containerDomNode.style.left = `${left}px`; this._hover.containerDomNode.style.top = `${Math.max(Math.round(top), 0)}px`; } + + private _onMouseLeave(e: MouseEvent): void { + const editorDomNode = this._editor.getDomNode(); + const isMousePositionOutsideOfEditor = !editorDomNode || !isMousePositionWithinElement(editorDomNode, e.x, e.y); + if (isMousePositionOutsideOfEditor) { + this.hide(); + } + } } From 08aeda78249c52daf6fff840d7940339c459ee30 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:04:34 -0700 Subject: [PATCH 676/798] chore: enable APIScan again (#224648) --- build/azure-pipelines/sdl-scan.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/build/azure-pipelines/sdl-scan.yml b/build/azure-pipelines/sdl-scan.yml index af20a305d9c..c28755b0522 100644 --- a/build/azure-pipelines/sdl-scan.yml +++ b/build/azure-pipelines/sdl-scan.yml @@ -130,18 +130,20 @@ steps: flattenFolders: true condition: succeeded() - # - task: APIScan@2 - # inputs: - # softwareFolder: $(agent.builddirectory)\scanbin - # softwareName: 'vscode-client' - # softwareVersionNum: '1' - # symbolsFolder: 'SRV*http://symweb;$(agent.builddirectory)\symbols' - # isLargeApp: false - # toolVersion: 'Latest' - # displayName: Run ApiScan - # condition: succeeded() - # env: - # AzureServicesAuthConnectionString: $(apiscan-connectionstring) + - task: APIScan@2 + inputs: + softwareFolder: $(agent.builddirectory)\scanbin + softwareName: 'vscode-client' + softwareVersionNum: '1' + symbolsFolder: 'srv*https://symweb.azurefd.net;$(agent.builddirectory)\symbols' + isLargeApp: false + toolVersion: 'Latest' + azureSubscription: 'vscode-apiscan' + displayName: Run ApiScan + condition: succeeded() + env: + AzureServicesAuthConnectionString: $(apiscan-connectionstring) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: PublishSecurityAnalysisLogs@3 inputs: From 145b650442fa582fe3e75efad885e9ca1e5dec37 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Aug 2024 18:23:02 +0200 Subject: [PATCH 677/798] esm - reduce diff (#224959) --- src/bootstrap-window.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index ee06b29d53c..a747a7cf5dc 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -110,7 +110,7 @@ const isESM = false; // DEV: For each CSS modules that we have we defined an entry in the import map that maps to // DEV: a blob URL that loads the CSS via a dynamic @import-rule. // DEV --------------------------------------------------------------------------------------- - if (configuration.cssModules) { + if (Array.isArray(configuration.cssModules) && configuration.cssModules.length > 0) { performance.mark('code/willAddCssLoader'); const style = document.createElement('style'); From 6288e5db959635c2409c90d45e12b457b17a9aef Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 6 Aug 2024 09:38:18 -0700 Subject: [PATCH 678/798] Re #209154. Make execution count sticky. (#224905) --- .../notebook/browser/notebookBrowser.ts | 1 + .../notebook/browser/notebookEditorWidget.ts | 35 +++++++-------- .../browser/view/cellParts/cellExecution.ts | 44 ++++++++++++++++--- .../viewParts/notebookEditorStickyScroll.ts | 13 +++++- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index b0abb66c5fb..5287bc53110 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -499,6 +499,7 @@ export interface INotebookEditor { readonly isDisposed: boolean; readonly activeKernel: INotebookKernel | undefined; readonly scrollTop: number; + readonly scrollBottom: number; readonly scopedContextKeyService: IContextKeyService; readonly activeCodeEditor: ICodeEditor | undefined; readonly codeEditors: [ICellViewModel, ICodeEditor][]; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 072a2a70593..27c63d82dda 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1065,27 +1065,22 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } private _registerNotebookStickyScroll() { - this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._list)); + this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._list, (sizeDelta) => { + if (this.isDisposed) { + return; + } - const localDisposableStore = this._register(new DisposableStore()); - - this._register(this._notebookStickyScroll.onDidChangeNotebookStickyScroll((sizeDelta) => { - const d = localDisposableStore.add(DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => { - if (this.isDisposed) { - return; + if (this._dimension && this._isVisible) { + if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking + this.layout(this._dimension); + this.setScrollTop(this.scrollTop + sizeDelta); + } else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing + this.setScrollTop(this.scrollTop + sizeDelta); + this.layout(this._dimension); } + } - if (this._dimension && this._isVisible) { - if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking - this.layout(this._dimension); - this.setScrollTop(this.scrollTop + sizeDelta); - } else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing - this.setScrollTop(this.scrollTop + sizeDelta); - this.layout(this._dimension); - } - } - localDisposableStore.delete(d); - })); + this._onDidScroll.fire(); })); } @@ -2100,6 +2095,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return this._list.scrollTop; } + get scrollBottom() { + return this._list.scrollTop + this._list.getRenderHeight(); + } + getAbsoluteTopOfElement(cell: ICellViewModel) { return this._list.getCellViewScrollTop(cell); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts index d5c27d01001..c69d62f34f8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts @@ -6,9 +6,11 @@ import * as DOM from 'vs/base/browser/dom'; import { disposableTimeout } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { clamp } from 'vs/base/common/numbers'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -39,6 +41,10 @@ export class CellExecutionPart extends CellContentPart { this.updateExecutionOrder(this.currentCell.internalMetadata); } })); + + this._register(this._notebookEditor.onDidScroll(() => { + this._updatePosition(); + })); } override didRenderCell(element: ICellViewModel): void { @@ -74,12 +80,38 @@ export class CellExecutionPart extends CellContentPart { } override updateInternalLayoutNow(element: ICellViewModel): void { - if (element.isInputCollapsed) { - DOM.hide(this._executionOrderLabel); - } else { - DOM.show(this._executionOrderLabel); - const top = element.layoutInfo.editorHeight - 22 + element.layoutInfo.statusBarHeight; - this._executionOrderLabel.style.top = `${top}px`; + this._updatePosition(); + } + + private _updatePosition() { + if (this.currentCell) { + if (this.currentCell.isInputCollapsed) { + DOM.hide(this._executionOrderLabel); + } else { + DOM.show(this._executionOrderLabel); + let top = this.currentCell.layoutInfo.editorHeight - 22 + this.currentCell.layoutInfo.statusBarHeight; + + if (this.currentCell instanceof CodeCellViewModel) { + const elementTop = this._notebookEditor.getAbsoluteTopOfElement(this.currentCell); + const editorBottom = elementTop + this.currentCell.layoutInfo.outputContainerOffset; + // another approach to avoid the flicker caused by sticky scroll is manually calculate the scrollBottom: + // const scrollBottom = this._notebookEditor.scrollTop + this._notebookEditor.getLayoutInfo().height - 26 - this._notebookEditor.getLayoutInfo().stickyHeight; + const scrollBottom = this._notebookEditor.scrollBottom; + + const lineHeight = 22; + if (scrollBottom <= editorBottom) { + const offset = editorBottom - scrollBottom; + top -= offset; + top = clamp( + top, + lineHeight + 12, // line height + padding for single line + this.currentCell.layoutInfo.editorHeight - lineHeight + this.currentCell.layoutInfo.statusBarHeight + ); + } + } + + this._executionOrderLabel.style.top = `${top}px`; + } } } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index 50a6758941b..e82cdf45abf 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -106,6 +106,8 @@ export class NotebookStickyScroll extends Disposable { readonly onDidChangeNotebookStickyScroll: Event = this._onDidChangeNotebookStickyScroll.event; private notebookCellOutlineReference?: IReference; + private readonly _layoutDisposableStore = this._register(new DisposableStore()); + getDomNode(): HTMLElement { return this.domNode; } @@ -143,6 +145,7 @@ export class NotebookStickyScroll extends Disposable { private readonly domNode: HTMLElement, private readonly notebookEditor: INotebookEditor, private readonly notebookCellList: INotebookCellList, + private readonly layoutFn: (delta: number) => void, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { @@ -269,8 +272,16 @@ export class NotebookStickyScroll extends Disposable { const sizeDelta = this.getCurrentStickyHeight() - oldStickyHeight; if (sizeDelta !== 0) { this._onDidChangeNotebookStickyScroll.fire(sizeDelta); + + const d = this._layoutDisposableStore.add(DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => { + this.layoutFn(sizeDelta); + this.updateDisplay(); + + this._layoutDisposableStore.delete(d); + })); + } else { + this.updateDisplay(); } - this.updateDisplay(); } private updateDisplay() { From ed167f4db7f6dcdf69dfafd2cc3eeded2a7833c8 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 6 Aug 2024 18:30:48 +0200 Subject: [PATCH 679/798] Fixes https://github.com/microsoft/vscode-copilot/issues/7122 --- .../components/diffEditorViewZones/diffEditorViewZones.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts index cfbad5b68b3..3b1b222de0d 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts @@ -633,7 +633,10 @@ export function allowsTrueInlineDiffRendering(mapping: DetailedLineRangeMapping) if (!mapping.innerChanges) { return false; } - return mapping.innerChanges.every(c => rangeIsSingleLine(c.modifiedRange) && rangeIsSingleLine(c.originalRange)); + return mapping.innerChanges.every(c => + (rangeIsSingleLine(c.modifiedRange) && rangeIsSingleLine(c.originalRange)) + || c.originalRange.equalsRange(new Range(1, 1, 1, 1)) + ); } function rangeIsSingleLine(range: Range): boolean { From 775e1e9bdf67f5f53f4485f296854c041567c00d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 6 Aug 2024 18:58:32 +0200 Subject: [PATCH 680/798] Trace log openTextDocument (#224962) Part of #6109 --- src/vs/workbench/api/common/extHost.api.impl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 98ab60ed9fb..c8664e19071 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1105,6 +1105,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return uriPromise.then(uri => { + extHostLogService.trace(`openTextDocument from ${extension.identifier}`); if (uri.scheme === Schemas.vscodeRemote && !uri.authority) { extHostApiDeprecation.report('workspace.openTextDocument', extension, `A URI of 'vscode-remote' scheme requires an authority.`); } From eae864677ebdc52e26661a2ee7ff94251a4c16cd Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 6 Aug 2024 19:06:01 +0200 Subject: [PATCH 681/798] refactoring the code --- .../contrib/hover/browser/hoverController.ts | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/hoverController.ts b/src/vs/editor/contrib/hover/browser/hoverController.ts index 2d4a15463ba..21b3281dd1d 100644 --- a/src/vs/editor/contrib/hover/browser/hoverController.ts +++ b/src/vs/editor/contrib/hover/browser/hoverController.ts @@ -149,14 +149,9 @@ export class HoverController extends Disposable implements IEditorContribution { } private _shouldNotHideCurrentHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { - if ( - this._isMouseOnContentHoverWidget(mouseEvent) + return this._isMouseOnContentHoverWidget(mouseEvent) || this._isMouseOnMarginHoverWidget(mouseEvent) - || this._isContentWidgetResizing() - ) { - return true; - } - return false; + || this._isContentWidgetResizing(); } private _isMouseOnMarginHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { @@ -200,35 +195,30 @@ export class HoverController extends Disposable implements IEditorContribution { const isHoverSticky = this._hoverSettings.sticky; - const isMouseOnStickyMarginHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean) => { + const isMouseOnStickyMarginHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean): boolean => { const isMouseOnMarginHoverWidget = this._isMouseOnMarginHoverWidget(mouseEvent); return isHoverSticky && isMouseOnMarginHoverWidget; }; - const isMouseOnStickyContentHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean) => { + const isMouseOnStickyContentHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean): boolean => { const isMouseOnContentHoverWidget = this._isMouseOnContentHoverWidget(mouseEvent); return isHoverSticky && isMouseOnContentHoverWidget; }; - const isMouseOnColorPicker = (mouseEvent: IEditorMouseEvent) => { + const isMouseOnColorPicker = (mouseEvent: IEditorMouseEvent): boolean => { const isMouseOnContentHoverWidget = this._isMouseOnContentHoverWidget(mouseEvent); - const isColorPickerVisible = this._contentWidget?.isColorPickerVisible; + const isColorPickerVisible = this._contentWidget?.isColorPickerVisible ?? false; return isMouseOnContentHoverWidget && isColorPickerVisible; }; // TODO@aiday-mar verify if the following is necessary code - const isTextSelectedWithinContentHoverWidget = (mouseEvent: IEditorMouseEvent, sticky: boolean) => { - return sticky + const isTextSelectedWithinContentHoverWidget = (mouseEvent: IEditorMouseEvent, sticky: boolean): boolean => { + return (sticky && this._contentWidget?.containsNode(mouseEvent.event.browserEvent.view?.document.activeElement) - && !mouseEvent.event.browserEvent.view?.getSelection()?.isCollapsed; + && !mouseEvent.event.browserEvent.view?.getSelection()?.isCollapsed) ?? false; }; - if ( - isMouseOnStickyMarginHoverWidget(mouseEvent, isHoverSticky) + return isMouseOnStickyMarginHoverWidget(mouseEvent, isHoverSticky) || isMouseOnStickyContentHoverWidget(mouseEvent, isHoverSticky) || isMouseOnColorPicker(mouseEvent) - || isTextSelectedWithinContentHoverWidget(mouseEvent, isHoverSticky) - ) { - return true; - } - return false; + || isTextSelectedWithinContentHoverWidget(mouseEvent, isHoverSticky); } private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void { From 93f019cec14238bf2e9d2a4cdb532217b2d4c49b Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 6 Aug 2024 10:34:09 -0700 Subject: [PATCH 682/798] Debt - Hook up a generic textual document highlight provider for single and multi file settings (#224884) * textual occurrence provider as a generic highlight provider * `[]` is valid, so change to undefined in test w invalid occurrece value --- .../browser/textualHighlightProvider.ts | 29 +++++- .../browser/wordHighlighter.ts | 89 +++---------------- .../api/browser/mainThreadLanguageFeatures.ts | 5 +- .../browser/extHostLanguageFeatures.test.ts | 2 +- 4 files changed, 43 insertions(+), 82 deletions(-) diff --git a/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts index 1d7a65695d3..adc31ecd8c0 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts @@ -5,7 +5,7 @@ import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/core/wordHelper'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { DocumentHighlight, DocumentHighlightKind, MultiDocumentHighlightProvider, ProviderResult } from 'vs/editor/common/languages'; +import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider, ProviderResult } from 'vs/editor/common/languages'; import { ITextModel } from 'vs/editor/common/model'; import { Position } from 'vs/editor/common/core/position'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -14,10 +14,33 @@ import { ResourceMap } from 'vs/base/common/map'; import { LanguageFilter } from 'vs/editor/common/languageSelector'; -class TextualDocumentHighlightProvider implements MultiDocumentHighlightProvider { +class TextualDocumentHighlightProvider implements DocumentHighlightProvider, MultiDocumentHighlightProvider { selector: LanguageFilter = { language: '*' }; + provideDocumentHighlights(model: ITextModel, position: Position, token: CancellationToken): ProviderResult { + const result: DocumentHighlight[] = []; + + const word = model.getWordAtPosition({ + lineNumber: position.lineNumber, + column: position.column + }); + + if (!word) { + return Promise.resolve(result); + } + + if (model.isDisposed()) { + return; + } + + const matches = model.findMatches(word.word, true, false, true, USUAL_WORD_SEPARATORS, false); + return matches.map(m => ({ + range: m.range, + kind: DocumentHighlightKind.Text + })); + } + provideMultiDocumentHighlights(primaryModel: ITextModel, position: Position, otherModels: ITextModel[], token: CancellationToken): ProviderResult> { const result = new ResourceMap(); @@ -57,7 +80,7 @@ export class TextualMultiDocumentHighlightFeature extends Disposable { @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { super(); - + this._register(languageFeaturesService.documentHighlightProvider.register('*', new TextualDocumentHighlightProvider())); this._register(languageFeaturesService.multiDocumentHighlightProvider.register('*', new TextualDocumentHighlightProvider())); } } diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index a566fd079de..6aa7ff831b6 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { CancelablePromise, createCancelablePromise, Delayer, first, timeout } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, Delayer, first } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -23,7 +22,7 @@ import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/commo import { IDiffEditor, IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; -import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider } from 'vs/editor/common/languages'; +import { DocumentHighlight, DocumentHighlightProvider, MultiDocumentHighlightProvider } from 'vs/editor/common/languages'; import { IModelDeltaDecoration, ITextModel, shouldSynchronizeModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { getHighlightDecorationOptions } from 'vs/editor/contrib/wordHighlighter/browser/highlightDecorations'; @@ -34,8 +33,8 @@ import { Schemas } from 'vs/base/common/network'; import { ResourceMap } from 'vs/base/common/map'; import { score } from 'vs/editor/common/languageSelector'; import { isEqual } from 'vs/base/common/resources'; -// import { TextualMultiDocumentHighlightFeature } from 'vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider'; -// import { registerEditorFeature } from 'vs/editor/common/editorFeatures'; +import { TextualMultiDocumentHighlightFeature } from 'vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider'; +import { registerEditorFeature } from 'vs/editor/common/editorFeatures'; const ctxHasWordHighlights = new RawContextKey('hasWordHighlights', false); @@ -44,11 +43,12 @@ export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry(orderedByScore.map(provider => () => { return Promise.resolve(provider.provideDocumentHighlights(model, position, token)) .then(undefined, onUnexpectedExternalError); - }), arrays.isNonEmptyArray).then(result => { + }), (result): result is DocumentHighlight[] => result !== undefined && result !== null).then(result => { if (result) { const map = new ResourceMap(); map.set(model.uri, result); @@ -63,17 +63,17 @@ export function getOccurrencesAcrossMultipleModels(registry: LanguageFeatureRegi // in order of score ask the occurrences provider // until someone response with a good result - // (good = none empty array) + // (good = non undefined and non null ResourceMap) + // (result of size == 0 is valid, no highlights is a valid/expected result -- not a signal to fall back to other providers) return first | null | undefined>(orderedByScore.map(provider => () => { const filteredModels = otherModels.filter(otherModel => { return shouldSynchronizeModel(otherModel); }).filter(otherModel => { return score(provider.selector, otherModel.uri, otherModel.getLanguageId(), true, undefined, undefined) > 0; }); - return Promise.resolve(provider.provideMultiDocumentHighlights(model, position, filteredModels, token)) .then(undefined, onUnexpectedExternalError); - }), (t: ResourceMap | null | undefined): t is ResourceMap => t instanceof ResourceMap && t.size > 0); + }), (result): result is ResourceMap => result !== undefined && result !== null); } interface IOccurenceAtPositionRequest { @@ -184,76 +184,13 @@ class MultiModelOccurenceRequest extends OccurenceAtPositionRequest { } } -class TextualOccurenceRequest extends OccurenceAtPositionRequest { - - private readonly _otherModels: ITextModel[]; - private readonly _selectionIsEmpty: boolean; - private readonly _word: IWordAtPosition | null; - - constructor(model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string, otherModels: ITextModel[]) { - super(model, selection, wordSeparators); - this._otherModels = otherModels; - this._selectionIsEmpty = selection.isEmpty(); - this._word = word; - } - - protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise> { - return timeout(250, token).then(() => { - const result = new ResourceMap(); - - let wordResult; - if (this._word) { - wordResult = this._word; - } else { - wordResult = model.getWordAtPosition(selection.getPosition()); - } - - if (!wordResult) { - return new ResourceMap(); - } - - const allModels = [model, ...this._otherModels]; - - for (const otherModel of allModels) { - if (otherModel.isDisposed()) { - continue; - } - - const matches = otherModel.findMatches(wordResult.word, true, false, true, wordSeparators, false); - const highlights = matches.map(m => ({ - range: m.range, - kind: DocumentHighlightKind.Text - })); - - if (highlights) { - result.set(otherModel.uri, highlights); - } - } - return result; - }); - } - - public override isValid(model: ITextModel, selection: Selection, decorations: IEditorDecorationsCollection): boolean { - const currentSelectionIsEmpty = selection.isEmpty(); - if (this._selectionIsEmpty !== currentSelectionIsEmpty) { - return false; - } - return super.isValid(model, selection, decorations); - } -} function computeOccurencesAtPosition(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string): IOccurenceAtPositionRequest { - if (registry.has(model)) { - return new SemanticOccurenceAtPositionRequest(model, selection, wordSeparators, registry); - } - return new TextualOccurenceRequest(model, selection, word, wordSeparators, []); + return new SemanticOccurenceAtPositionRequest(model, selection, wordSeparators, registry); } function computeOccurencesMultiModel(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string, otherModels: ITextModel[]): IOccurenceAtPositionRequest { - if (registry.has(model)) { - return new MultiModelOccurenceRequest(model, selection, wordSeparators, registry, otherModels); - } - return new TextualOccurenceRequest(model, selection, word, wordSeparators, otherModels); + return new MultiModelOccurenceRequest(model, selection, wordSeparators, registry, otherModels); } registerModelAndPositionCommand('_executeDocumentHighlights', async (accessor, model, position) => { @@ -973,4 +910,4 @@ registerEditorContribution(WordHighlighterContribution.ID, WordHighlighterContri registerEditorAction(NextWordHighlightAction); registerEditorAction(PrevWordHighlightAction); registerEditorAction(TriggerWordHighlightAction); -// registerEditorFeature(TextualMultiDocumentHighlightFeature); +registerEditorFeature(TextualMultiDocumentHighlightFeature); diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index c3d02eaf472..629af25ae4c 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { createStringDataTransferItem, IReadonlyVSDataTransfer, VSDataTransfer } from 'vs/base/common/dataTransfer'; @@ -321,7 +320,9 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread selector: selector, provideMultiDocumentHighlights: (model: ITextModel, position: EditorPosition, otherModels: ITextModel[], token: CancellationToken): Promise | undefined> => { return this._proxy.$provideMultiDocumentHighlights(handle, model.uri, position, otherModels.map(model => model.uri), token).then(dto => { - if (isFalsyOrEmpty(dto)) { + // dto should be non-null + non-undefined + // dto length of 0 is valid, just no highlights, pass this through. + if (dto === undefined || dto === null) { return undefined; } const result = new ResourceMap(); diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 141f114ebfa..ec934301cd9 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -517,7 +517,7 @@ suite('ExtHostLanguageFeatures', function () { disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider { provideDocumentHighlights(): any { - return []; + return undefined; } })); disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, '*', new class implements vscode.DocumentHighlightProvider { From 3b0671e73ab2cf5852cfad5dc87e66b08578b959 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 6 Aug 2024 10:38:22 -0700 Subject: [PATCH 683/798] Enable notebook smoke test for electron only (#224968) --- test/smoke/src/areas/notebook/notebook.test.ts | 2 +- test/smoke/src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts index 130b49a1473..da4d527fe4f 100644 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -34,7 +34,7 @@ export function setup(logger: Logger) { }); }); - it.skip('check object leaks', async function () { + it('check object leaks', async function () { const app = this.app as Application; await app.profiler.checkObjectLeaks(['NotebookTextModel', 'NotebookCellTextModel', 'NotebookEventDispatcher'], async () => { await app.workbench.notebook.openNotebook(); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 78e95650db7..4df38db9d72 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -400,7 +400,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { if (!opts.web) { setupDataLossTests(() => opts['stable-build'] /* Do not change, deferred for a reason! */, logger); } setupPreferencesTests(logger); setupSearchTests(logger); - setupNotebookTests(logger); + if (!opts.web) { setupNotebookTests(logger); } setupLanguagesTests(logger); setupTerminalTests(logger); setupTaskTests(logger); From 8087dd8146f3d393187ee0c3b45f5b5c2e0410e5 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 6 Aug 2024 10:57:39 -0700 Subject: [PATCH 684/798] Fix reading null WH query modelInfo (#224969) fix reading null modelInfo --- .../editor/contrib/wordHighlighter/browser/wordHighlighter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index 6aa7ff831b6..22bd3944e14 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -671,7 +671,7 @@ 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.modelInfo || WordHighlighter.query.modelInfo.model.isDisposed()) { + if (!WordHighlighter.query || !WordHighlighter.query.modelInfo || WordHighlighter.query.modelInfo.model.isDisposed()) { return; } this.workerRequest = this.computeWithModel(WordHighlighter.query.modelInfo.model, WordHighlighter.query.modelInfo.selection, WordHighlighter.query.word, otherModelsToHighlight); From ab962cd4225b3ba21a5f9e7855ce15ddca2da59e Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 6 Aug 2024 14:28:43 -0700 Subject: [PATCH 685/798] ensure dependency tasks are removed from `activeTasks` (#224984) ensure dependency tasks are removed from activeTasks --- src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 117501cdadd..6919111b4c6 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -562,9 +562,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return { exitCode: 0 }; }); }).finally(() => { - if (this._activeTasks[mapKey] === activeTask) { - delete this._activeTasks[mapKey]; - } + delete this._activeTasks[mapKey]; }); const lastInstance = this._getInstances(task).pop(); const count = lastInstance?.count ?? { count: 0 }; From cd340e6aa44bd9836210a746ab18f57cf1ed032d Mon Sep 17 00:00:00 2001 From: "Matthias B." Date: Tue, 6 Aug 2024 23:54:05 +0200 Subject: [PATCH 686/798] Fix: only add apt sources for users that want them (#22145) (#221285) Signed-off-by: Matthias Breithaupt --- build/gulpfile.vscode.linux.js | 6 +++- resources/linux/debian/postinst.template | 36 +++++++++++++++++++---- resources/linux/debian/postrm.template | 19 ++++++++++++ resources/linux/debian/templates.template | 6 ++++ 4 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 resources/linux/debian/templates.template diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 28ddfb04c3d..79594543c3b 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -108,7 +108,11 @@ function prepareDebPackage(arch) { .pipe(replace('@@NAME@@', product.applicationName)) .pipe(rename('DEBIAN/postinst')); - const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); + const templates = gulp.src('resources/linux/debian/templates.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('DEBIAN/templates')); + + const all = es.merge(control, templates, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); return all.pipe(vfs.dest(destination)); }; diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index 16acb1481bf..7dc5bef8c5c 100755 --- a/resources/linux/debian/postinst.template +++ b/resources/linux/debian/postinst.template @@ -35,20 +35,46 @@ if [ "@@NAME@@" != "code-oss" ]; then eval $(apt-config shell APT_TRUSTED_PARTS Dir::Etc::trustedparts/d) CODE_TRUSTED_PART=${APT_TRUSTED_PARTS}microsoft.gpg + RET=true + if [ -e '/usr/share/debconf/confmodule' ]; then + . /usr/share/debconf/confmodule + db_get @@NAME@@/add-microsoft-repo || true + fi + # Install repository source list WRITE_SOURCE=0 - if [ ! -f $CODE_SOURCE_PART ] && [ ! -f /etc/rpi-issue ]; then - # Write source list if it does not exist and we're not running on Raspberry Pi OS - WRITE_SOURCE=1 - elif grep -Eq "http:\/\/packages\.microsoft\.com\/repos\/vscode" $CODE_SOURCE_PART; then + if [ "$RET" = false ]; then + # The user does not want to add the microsoft repository + WRITE_SOURCE=0 + elif grep -q "http://packages.microsoft.com/repos/vscode" $CODE_SOURCE_PART; then # Migrate from old repository + WRITE_SOURCE=2 + elif grep -q "http://packages.microsoft.com/repos/code" $CODE_SOURCE_PART; then + # Migrate from old repository + WRITE_SOURCE=2 + elif apt-cache policy | grep -q "https://packages.microsoft.com/repos/code"; then + # Skip following checks if the repo is already known to apt + WRITE_SOURCE=0 + elif [ ! -f $CODE_SOURCE_PART ] && [ ! -f /etc/rpi-issue ]; then + # Write source list if it does not exist and we're not running on Raspberry Pi OS WRITE_SOURCE=1 elif grep -q "# disabled on upgrade to" $CODE_SOURCE_PART; then # Write source list if it was disabled by OS upgrade WRITE_SOURCE=1 fi - if [ "$WRITE_SOURCE" -eq "1" ]; then + if [ "$WRITE_SOURCE" -eq "1" ] && [ -e '/usr/share/debconf/confmodule' ]; then + # Ask the user whether to actually write the source list + db_input high @@NAME@@/add-microsoft-repo || true + db_go || true + + db_get @@NAME@@/add-microsoft-repo + if [ "$RET" = false ]; then + WRITE_SOURCE=0 + fi + fi + + if [ "$WRITE_SOURCE" -ne "0" ]; then echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### # You may comment out this entry, but any other modifications may be lost. deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/repos/code stable main" > $CODE_SOURCE_PART diff --git a/resources/linux/debian/postrm.template b/resources/linux/debian/postrm.template index fb36d522f38..dcbfda95ea0 100755 --- a/resources/linux/debian/postrm.template +++ b/resources/linux/debian/postrm.template @@ -14,3 +14,22 @@ fi if hash update-mime-database 2>/dev/null; then update-mime-database /usr/share/mime fi + +RET=true +if [ -e '/usr/share/debconf/confmodule' ]; then + . /usr/share/debconf/confmodule + db_get @@NAME@@/add-microsoft-repo || true +fi +if [ "$RET" = "true" ]; then + eval $(apt-config shell APT_SOURCE_PARTS Dir::Etc::sourceparts/d) + CODE_SOURCE_PART=${APT_SOURCE_PARTS}vscode.list + rm -f $CODE_SOURCE_PART + + eval $(apt-config shell APT_TRUSTED_PARTS Dir::Etc::trustedparts/d) + CODE_TRUSTED_PART=${APT_TRUSTED_PARTS}microsoft.gpg + rm -f $CODE_TRUSTED_PART +fi + +if [ "$1" = "purge" ] && [ -e '/usr/share/debconf/confmodule' ]; then + db_purge +fi diff --git a/resources/linux/debian/templates.template b/resources/linux/debian/templates.template new file mode 100644 index 00000000000..7be5e039b26 --- /dev/null +++ b/resources/linux/debian/templates.template @@ -0,0 +1,6 @@ +Template: @@NAME@@/add-microsoft-repo +Type: boolean +Default: true +Description: Add Microsoft apt repository for Visual Studio Code? + The installer would like to add the Microsoft repository and signing + key to update VS Code through apt. From b7e421b7347eb4c6c3991ecdfceb68dc1f684cd8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 6 Aug 2024 16:04:21 -0700 Subject: [PATCH 687/798] Fix arguments for create `tsconfig`/`jsconfig` (#224990) Fix arguments for create tsconfig/jsconfig Fixes #224989 Adds typings too to prevent this again --- .../src/ui/intellisenseStatus.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/ui/intellisenseStatus.ts b/extensions/typescript-language-features/src/ui/intellisenseStatus.ts index 1a6ea63f427..e26e2b3719f 100644 --- a/extensions/typescript-language-features/src/ui/intellisenseStatus.ts +++ b/extensions/typescript-language-features/src/ui/intellisenseStatus.ts @@ -43,6 +43,8 @@ namespace IntellisenseState { export type State = typeof None | Pending | Resolved | typeof SyntaxOnly; } +type CreateOrOpenConfigCommandArgs = [root: vscode.Uri, projectType: ProjectType]; + export class IntellisenseStatus extends Disposable { public readonly openOpenConfigCommandId = '_typescript.openConfig'; @@ -62,7 +64,7 @@ export class IntellisenseStatus extends Disposable { commandManager.register({ id: this.openOpenConfigCommandId, - execute: async (root: vscode.Uri, projectType: ProjectType) => { + execute: async (...[root, projectType]: CreateOrOpenConfigCommandArgs) => { if (this._state.type === IntellisenseState.Type.Resolved) { await openProjectConfigOrPromptToCreate(projectType, this._client, root, this._state.configFile); } else if (this._state.type === IntellisenseState.Type.Pending) { @@ -72,7 +74,7 @@ export class IntellisenseStatus extends Disposable { }); commandManager.register({ id: this.createOrOpenConfigCommandId, - execute: async (root: vscode.Uri, projectType: ProjectType) => { + execute: async (...[root, projectType]: CreateOrOpenConfigCommandArgs) => { await openOrCreateConfig(this._client.apiVersion, projectType, root, this._client.configuration); }, }); @@ -182,7 +184,7 @@ export class IntellisenseStatus extends Disposable { title: this._state.projectType === ProjectType.TypeScript ? vscode.l10n.t("Configure tsconfig") : vscode.l10n.t("Configure jsconfig"), - arguments: [rootPath], + arguments: [rootPath, this._state.projectType] satisfies CreateOrOpenConfigCommandArgs, }; } else { statusItem.text = vscode.workspace.asRelativePath(this._state.configFile); @@ -190,7 +192,7 @@ export class IntellisenseStatus extends Disposable { statusItem.command = { command: this.openOpenConfigCommandId, title: vscode.l10n.t("Open config file"), - arguments: [rootPath], + arguments: [rootPath, this._state.projectType] satisfies CreateOrOpenConfigCommandArgs, }; } break; From 1ef4060adfa07fd70b0dae0ed899d891d76333b6 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 7 Aug 2024 09:14:45 +1000 Subject: [PATCH 688/798] Soft revert of support for #224759 (#224986) Soft revert of support for 224759 --- .../notebook/browser/diff/diffComponents.ts | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 04488e0f8f6..e3124e60c20 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -723,43 +723,44 @@ abstract class AbstractElementRenderer extends Disposable { abstract layout(state: IDiffElementLayoutState): void; protected updateEditorOptions(editor: DiffEditorWidget, optionsToUpdate: ('hideUnchangedRegions' | 'renderSideBySide' | 'useInlineViewWhenSpaceIsLimited')[]): IDisposable { - if (!optionsToUpdate.length) { - return Disposable.None; - } + return Disposable.None; + // if (!optionsToUpdate.length) { + // return Disposable.None; + // } - const options: { - renderSideBySide?: boolean; - useInlineViewWhenSpaceIsLimited?: boolean; - hideUnchangedRegions?: { enabled: boolean }; - } = {}; + // const options: { + // renderSideBySide?: boolean; + // useInlineViewWhenSpaceIsLimited?: boolean; + // hideUnchangedRegions?: { enabled: boolean }; + // } = {}; - if (optionsToUpdate.includes('renderSideBySide')) { - options.renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); - } - if (optionsToUpdate.includes('hideUnchangedRegions')) { - const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); - options.hideUnchangedRegions = { enabled }; - } - if (optionsToUpdate.includes('useInlineViewWhenSpaceIsLimited')) { - options.useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); - } + // if (optionsToUpdate.includes('renderSideBySide')) { + // options.renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); + // } + // if (optionsToUpdate.includes('hideUnchangedRegions')) { + // const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + // options.hideUnchangedRegions = { enabled }; + // } + // if (optionsToUpdate.includes('useInlineViewWhenSpaceIsLimited')) { + // options.useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); + // } - editor.updateOptions(options); + // editor.updateOptions(options); - return this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('diffEditor.hideUnchangedRegions.enabled')) { - const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); - editor.updateOptions({ hideUnchangedRegions: { enabled } }); - } - if (e.affectsConfiguration('diffEditor.renderSideBySide')) { - const renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); - editor.updateOptions({ renderSideBySide }); - } - if (e.affectsConfiguration('diffEditor.useInlineViewWhenSpaceIsLimited')) { - const useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); - editor.updateOptions({ useInlineViewWhenSpaceIsLimited }); - } - }); + // return this.configurationService.onDidChangeConfiguration(e => { + // if (e.affectsConfiguration('diffEditor.hideUnchangedRegions.enabled')) { + // const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + // editor.updateOptions({ hideUnchangedRegions: { enabled } }); + // } + // if (e.affectsConfiguration('diffEditor.renderSideBySide')) { + // const renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); + // editor.updateOptions({ renderSideBySide }); + // } + // if (e.affectsConfiguration('diffEditor.useInlineViewWhenSpaceIsLimited')) { + // const useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); + // editor.updateOptions({ useInlineViewWhenSpaceIsLimited }); + // } + // }); } } From 10487da7f3dd85b85fd28cd4cdc4eb2a955c697b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 6 Aug 2024 16:42:19 -0700 Subject: [PATCH 689/798] Fix progress task with empty message (#224995) --- src/vs/workbench/api/common/extHostChatAgents2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 3e5225b8dee..b366aa2a986 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -93,7 +93,7 @@ class ChatAgentResponseStream { }; Promise.all([progressReporterPromise, task?.(progressReporter)]).then(([handle, res]) => { - if (handle !== undefined && res !== undefined) { + if (handle !== undefined) { this._proxy.$handleProgressChunk(this._request.requestId, typeConvert.ChatTaskResult.from(res), handle); } }); From fedcaf3e4baef664f6ca63a184e5b50f85e4f444 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 7 Aug 2024 11:01:03 +1000 Subject: [PATCH 690/798] Rename `Cell` to `Input` in nb diff view (#224988) --- .../notebook/browser/diff/diffComponents.ts | 6 +++--- .../contrib/notebook/browser/diff/notebookDiff.css | 14 +++++++------- .../notebook/browser/diff/notebookDiffList.ts | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index e3124e60c20..2aaac1987db 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -1596,9 +1596,9 @@ export class ModifiedElement extends AbstractElementRenderer { }, getFoldingState: (cell) => cell.cellFoldingState, updateFoldingState: (cell, state) => cell.cellFoldingState = state, - unChangedLabel: 'Cell', - changedLabel: 'Cell changed', - prefix: 'cell', + unChangedLabel: 'Input', + changedLabel: 'Input changed', + prefix: 'input', menuId: MenuId.NotebookDiffCellInputTitle } )); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index 40de8285bca..d59e7471d5b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -99,7 +99,7 @@ width: 50%; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { display: flex; @@ -108,7 +108,7 @@ cursor: default; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-folding-indicator .codicon, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-folding-indicator .codicon, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-folding-indicator .codicon, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-folding-indicator .codicon { visibility: visible; @@ -116,7 +116,7 @@ cursor: pointer; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { display: flex; @@ -124,26 +124,26 @@ align-items: center; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-toolbar, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-toolbar, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-toolbar, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-toolbar { margin-left: auto; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-status, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-status, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status { font-size: 12px; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-status span, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-status span, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status span, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status span { margin: 0 0 0 8px; line-height: 21px; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-status span.property-description, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-status span.property-description, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status span.property-description, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status span.property-description { font-style: italic; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index e2edb9b1b27..a12cb9c2fe6 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -82,7 +82,7 @@ export class CellDiffSingleSideRenderer implements IListRenderer Date: Tue, 6 Aug 2024 18:48:14 -0700 Subject: [PATCH 691/798] Update conpty.dll Part of microsoft/vscode#224488 --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 79877cd05a5..27c982781e1 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta18", + "node-pty": "1.1.0-beta19", "open": "^8.4.2", "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", diff --git a/remote/package.json b/remote/package.json index 40b947ae26f..0f3c8d72d22 100644 --- a/remote/package.json +++ b/remote/package.json @@ -30,7 +30,7 @@ "kerberos": "2.1.1-alpha.0", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta18", + "node-pty": "1.1.0-beta19", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index 862f201750a..2989156660d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -484,10 +484,10 @@ node-gyp-build@4.8.1, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== -node-pty@1.1.0-beta18: - version "1.1.0-beta18" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta18.tgz#8ec680788a491423e93096fb1235df0c9079451a" - integrity sha512-H3b5Z9EaMRfKtcz7K5vaIBXLKg0OG+Rz0DjfpUBG9yS2XSbm6Ve4/RjqiJTMJUgp7l2s+ymHCHOp+J1fpDRiHw== +node-pty@1.1.0-beta19: + version "1.1.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta19.tgz#a74dc04429903c5ac49ee81a15a24590da67d4f3" + integrity sha512-/p4Zu56EYDdXjjaLWzrIlFyrBnND11LQGP0/L6GEVGURfCNkAlHc3Twg/2I4NPxghimHXgvDlwp7Z2GtvDIh8A== dependencies: node-addon-api "^7.1.0" diff --git a/yarn.lock b/yarn.lock index 6e460611329..a751a47f79a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7473,10 +7473,10 @@ node-html-parser@^6.1.1: css-select "^5.1.0" he "1.2.0" -node-pty@1.1.0-beta18: - version "1.1.0-beta18" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta18.tgz#8ec680788a491423e93096fb1235df0c9079451a" - integrity sha512-H3b5Z9EaMRfKtcz7K5vaIBXLKg0OG+Rz0DjfpUBG9yS2XSbm6Ve4/RjqiJTMJUgp7l2s+ymHCHOp+J1fpDRiHw== +node-pty@1.1.0-beta19: + version "1.1.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta19.tgz#a74dc04429903c5ac49ee81a15a24590da67d4f3" + integrity sha512-/p4Zu56EYDdXjjaLWzrIlFyrBnND11LQGP0/L6GEVGURfCNkAlHc3Twg/2I4NPxghimHXgvDlwp7Z2GtvDIh8A== dependencies: node-addon-api "^7.1.0" From 7380dcb04abedfec5d66afa3c6d348d22c707d02 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Aug 2024 09:07:02 +0200 Subject: [PATCH 692/798] api todos and notebook date update (#225016) --- .vscode/notebooks/api.github-issues | 2 +- src/vscode-dts/vscode.proposed.extensionsAny.d.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index c5dda6b26af..c402cca3836 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"July 2024\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"August 2024\"" }, { "kind": 1, diff --git a/src/vscode-dts/vscode.proposed.extensionsAny.d.ts b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts index c53cd6d89f8..468ffe7b78c 100644 --- a/src/vscode-dts/vscode.proposed.extensionsAny.d.ts +++ b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts @@ -28,6 +28,7 @@ declare module 'vscode' { * @return An extension or `undefined`. */ export function getExtension(extensionId: string, includeDifferentExtensionHosts: boolean): Extension | undefined; + export function getExtension(extensionId: string, includeDifferentExtensionHosts: true): Extension | undefined; /** * All extensions across all extension hosts. From 1925ac59ef47f38e214814f38febf215ca144ce0 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Wed, 7 Aug 2024 00:10:18 -0700 Subject: [PATCH 693/798] add index to suggestions telemetry (#225001) * add some telemetry * add telemetry data * cleanup and revert some changes --- .../contrib/suggest/browser/suggestController.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 31eb7c7ea5f..0e29ea762bc 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -509,15 +509,14 @@ export class SuggestController implements IEditorContribution { // clear only now - after all tasks are done Promise.all(tasks).finally(() => { - this._reportSuggestionAcceptedTelemetry(item, model, isResolved, _commandExectionDuration, _additionalEditsAppliedAsync); + this._reportSuggestionAcceptedTelemetry(item, model, isResolved, _commandExectionDuration, _additionalEditsAppliedAsync, event.index); this.model.clear(); cts.dispose(); }); } - private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, itemResolved: boolean, commandExectionDuration: number, additionalEditsAppliedAsync: number) { - + private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, itemResolved: boolean, commandExectionDuration: number, additionalEditsAppliedAsync: number, index: number): void { if (Math.floor(Math.random() * 100) === 0) { // throttle telemetry event because accepting completions happens a lot return; @@ -529,6 +528,8 @@ export class SuggestController implements IEditorContribution { resolveInfo: number; resolveDuration: number; commandDuration: number; additionalEditsAsync: number; + index: number; + label: string; }; type AcceptedSuggestionClassification = { owner: 'jrieken'; @@ -543,6 +544,8 @@ export class SuggestController implements IEditorContribution { resolveDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How long resolving took to finish' }; commandDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How long a completion item command took' }; additionalEditsAsync: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Info about asynchronously applying additional edits' }; + index: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The order of the completion item in the list.' }; + label: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The label of the completion item.' }; }; this._telemetryService.publicLog2('suggest.acceptedSuggestion', { @@ -555,7 +558,9 @@ export class SuggestController implements IEditorContribution { resolveInfo: !item.provider.resolveCompletionItem ? -1 : itemResolved ? 1 : 0, resolveDuration: item.resolveDuration, commandDuration: commandExectionDuration, - additionalEditsAsync: additionalEditsAppliedAsync + additionalEditsAsync: additionalEditsAppliedAsync, + index, + label: item.textLabel }); } From 4a8eb1a4af456c23aceb640c6909fa051f8024b4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 Aug 2024 09:24:13 +0200 Subject: [PATCH 694/798] Content lost on failed save (fix #224820) (#225020) --- .../browser/editors/textFileSaveErrorHandler.ts | 8 ++++---- .../contrib/files/browser/fileCommands.ts | 15 ++++++++------- .../workingCopy/common/storedFileWorkingCopy.ts | 6 +++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 066e0372c6b..a598f8b7b2e 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -158,8 +158,8 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa // Save As primaryActions.push(this.instantiationService.createInstance(SaveModelAsAction, model)); - // Discard - primaryActions.push(this.instantiationService.createInstance(DiscardModelAction, model)); + // Revert + primaryActions.push(this.instantiationService.createInstance(RevertModelAction, model)); // Message if (isWriteLocked) { @@ -306,12 +306,12 @@ class RetrySaveModelAction extends Action { } } -class DiscardModelAction extends Action { +class RevertModelAction extends Action { constructor( private model: ITextFileEditorModel ) { - super('workbench.files.action.discardModel', localize('discard', "Discard")); + super('workbench.files.action.revertModel', localize('revert', "Revert")); } override async run(): Promise { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index bfa8d97796c..1311bd1839b 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -39,7 +39,7 @@ import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/em import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { isCancellationError } from 'vs/base/common/errors'; -import { toAction } from 'vs/base/common/actions'; +import { IAction, toAction } from 'vs/base/common/actions'; import { EditorOpenSource, EditorResolution } from 'vs/platform/editor/common/editor'; import { hash } from 'vs/base/common/hash'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -444,16 +444,17 @@ async function doSaveEditors(accessor: ServicesAccessor, editors: IEditorIdentif await editorService.save(editors, options); } catch (error) { if (!isCancellationError(error)) { + const actions: IAction[] = [toAction({ id: 'workbench.action.files.saveEditors', label: nls.localize('retry', "Retry"), run: () => instantiationService.invokeFunction(accessor => doSaveEditors(accessor, editors, options)) })]; + const editorsToRevert = editors.filter(({ editor }) => !editor.hasCapability(EditorInputCapabilities.Untitled) /* all except untitled to prevent unexpected data-loss */); + if (editorsToRevert.length > 0) { + actions.push(toAction({ id: 'workbench.action.files.revertEditors', label: editorsToRevert.length > 1 ? nls.localize('revertAll', "Revert All") : nls.localize('revert', "Revert"), run: () => editorService.revert(editorsToRevert) })); + } + notificationService.notify({ id: editors.map(({ editor }) => hash(editor.resource?.toString())).join(), // ensure unique notification ID per set of editor severity: Severity.Error, message: nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false)), - actions: { - primary: [ - toAction({ id: 'workbench.action.files.saveEditors', label: nls.localize('retry', "Retry"), run: () => instantiationService.invokeFunction(accessor => doSaveEditors(accessor, editors, options)) }), - toAction({ id: 'workbench.action.files.revertEditors', label: nls.localize('discard', "Discard"), run: () => editorService.revert(editors) }) - ] - } + actions: { primary: actions } }); } } diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts index b7753b5342f..80148112acc 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts @@ -1143,7 +1143,7 @@ export class StoredFileWorkingCopy extend message = localize('staleSaveError', "Failed to save '{0}': The content of the file is newer. Do you want to overwrite the file with your changes?", this.name); primaryActions.push(toAction({ id: 'fileWorkingCopy.overwrite', label: localize('overwrite', "Overwrite"), run: () => this.save({ ...options, ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }) })); - primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('discard', "Discard"), run: () => this.revert() })); + primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('revert', "Revert"), run: () => this.revert() })); } // Any other save error @@ -1196,8 +1196,8 @@ export class StoredFileWorkingCopy extend } })); - // Discard - primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('discard', "Discard"), run: () => this.revert() })); + // Revert + primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('revert', "Revert"), run: () => this.revert() })); // Message if (isWriteLocked) { From a2547631a8c71013c43ce68a4fab700fbade429e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Aug 2024 11:07:36 +0200 Subject: [PATCH 695/798] make amdX more graceful when encountering faux AMD (#225017) * make amdX more graceful when encountering faux AMD * fix monaco --- src/vs/amdX.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts index 8433284d976..eebe82fe16c 100644 --- a/src/vs/amdX.ts +++ b/src/vs/amdX.ts @@ -84,7 +84,9 @@ class AMDModuleImporter { this._initialize(); const defineCall = await (this._isWebWorker ? this._workerLoadScript(scriptSrc) : this._isRenderer ? this._rendererLoadScript(scriptSrc) : this._nodeJSLoadScript(scriptSrc)); if (!defineCall) { - throw new Error(`Did not receive a define call from script ${scriptSrc}`); + // throw new Error(`Did not receive a define call from script ${scriptSrc}`); + console.warn(`Did not receive a define call from script ${scriptSrc}`); + return undefined; } // TODO require, exports, module if (Array.isArray(defineCall.dependencies) && defineCall.dependencies.length > 0) { From 6b0fa93c8176a2bbfdd7ca6da7a0b62a157e16bd Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:20:56 +0200 Subject: [PATCH 696/798] Git - extension should only open repositories for resources with the `file` scheme (#225024) --- extensions/git/src/api/api1.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index f94ecbab7b0..332c328b2d1 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -322,6 +322,10 @@ export class ApiImpl implements API { } async openRepository(root: Uri): Promise { + if (root.scheme !== 'file') { + return null; + } + await this._model.openRepository(root.fsPath); return this.getRepository(root) || null; } From 69647f087c13a082b7c324b0eb6d7ad0a3276a0c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 7 Aug 2024 11:23:48 +0200 Subject: [PATCH 697/798] testWSLFeatureInstalled: spawn EPERM (#225025) --- src/vs/platform/remote/node/wsl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/remote/node/wsl.ts b/src/vs/platform/remote/node/wsl.ts index 4bc71a35f0c..637ccff4fb0 100644 --- a/src/vs/platform/remote/node/wsl.ts +++ b/src/vs/platform/remote/node/wsl.ts @@ -26,7 +26,11 @@ async function testWSLFeatureInstalled(): Promise { const wslExePath = getWSLExecutablePath(); if (wslExePath) { return new Promise(s => { - cp.execFile(wslExePath, ['--status'], err => s(!err)); + try { + cp.execFile(wslExePath, ['--status'], err => s(!err)); + } catch (e) { + s(false); + } }); } } else { From 911602a8b8b737e689e1a0f838e0b93a936ec86b Mon Sep 17 00:00:00 2001 From: troy351 <914053923@qq.com> Date: Wed, 7 Aug 2024 18:04:28 +0800 Subject: [PATCH 698/798] fix: multiDiffEditor has wrong background color name (#224151) use correct color name Co-authored-by: Martin Aeschlimann --- src/vs/editor/browser/widget/multiDiffEditor/colors.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/multiDiffEditor/colors.ts b/src/vs/editor/browser/widget/multiDiffEditor/colors.ts index 297e5e86465..c9e2cca5fb6 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/colors.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/colors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorBackground } from 'vs/platform/theme/common/colorRegistry'; export const multiDiffEditorHeaderBackground = registerColor( 'multiDiffEditor.headerBackground', @@ -14,7 +14,7 @@ export const multiDiffEditorHeaderBackground = registerColor( export const multiDiffEditorBackground = registerColor( 'multiDiffEditor.background', - 'editorBackground', + editorBackground, localize('multiDiffEditor.background', 'The background color of the multi file diff editor') ); From a135d55db1397955183b62adafa94ecde2f1db15 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 7 Aug 2024 20:24:14 +1000 Subject: [PATCH 699/798] Add `Input` cell headers for Inserted/Deleted cells (#225027) * Add `Input` cell headers for Inserted/Deleted cells * Same paddig for all cells --- .../notebook/browser/diff/diffComponents.ts | 219 ++++++++++++------ .../notebook/browser/diff/notebookDiff.css | 2 + .../browser/diff/notebookDiffEditorBrowser.ts | 1 + .../notebook/browser/diff/notebookDiffList.ts | 9 +- 4 files changed, 148 insertions(+), 83 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 2aaac1987db..e9f396c4c1a 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -43,6 +43,7 @@ import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibil import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel'; export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { return { @@ -240,6 +241,7 @@ abstract class AbstractElementRenderer extends Disposable { protected _ignoreMetadata: boolean = false; protected _ignoreOutputs: boolean = false; protected _cellHeaderContainer!: HTMLElement; + protected _editorContainer!: HTMLElement; protected _cellHeader!: PropertyHeader; protected _metadataHeaderContainer!: HTMLElement; protected _metadataHeader!: PropertyHeader; @@ -288,7 +290,9 @@ abstract class AbstractElementRenderer extends Disposable { this._isDisposed = false; this._metadataEditorDisposeStore = this._register(new DisposableStore()); this._outputEditorDisposeStore = this._register(new DisposableStore()); - this._register(cell.onDidLayoutChange(e => this.layout(e))); + this._register(cell.onDidLayoutChange(e => { + this.layout(e); + })); this._register(cell.onDidLayoutChange(e => this.updateBorders())); this.init(); this.buildBody(); @@ -765,10 +769,12 @@ abstract class AbstractElementRenderer extends Disposable { } abstract class SingleSideDiffElement extends AbstractElementRenderer { - + protected _editor!: CodeEditorWidget; override readonly cell: SingleSideDiffElementViewModel; override readonly templateData: CellDiffSingleSideRenderTemplate; - + abstract get nestedCellViewModel(): DiffNestedCellViewModel; + abstract get changedLabel(): string; + abstract get isEditable(): boolean; constructor( notebookEditor: INotebookTextDiffEditor, cell: SingleSideDiffElementViewModel, @@ -874,6 +880,105 @@ abstract class SingleSideDiffElement extends AbstractElementRenderer { })); } + override updateSourceEditor(): void { + this._cellHeaderContainer = this.templateData.cellHeaderContainer; + this._cellHeaderContainer.style.display = 'flex'; + this._cellHeaderContainer.innerText = ''; + this._editorContainer = this.templateData.editorContainer; + this._editorContainer.classList.add('diff'); + + const renderSourceEditor = () => { + if (this.cell.cellFoldingState === PropertyFoldingState.Collapsed) { + this._editorContainer.style.display = 'none'; + 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; + + this._editorContainer.style.height = `${editorHeight}px`; + this._editorContainer.style.display = 'block'; + + if (this._editor) { + const contentHeight = this._editor.getContentHeight(); + if (contentHeight >= 0) { + this.cell.editorHeight = contentHeight; + } + return; + } + + this._editor = this.templateData.sourceEditor; + this._editor.layout( + { + width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, + height: editorHeight + } + ); + if (this.isEditable) { + this._editor.updateOptions({ readOnly: false }); + } + this.cell.editorHeight = editorHeight; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (this.cell.cellFoldingState === PropertyFoldingState.Expanded && e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) { + this.cell.editorHeight = e.contentHeight; + } + })); + this._initializeSourceDiffEditor(this.nestedCellViewModel); + }; + + this._cellHeader = this._register(this.instantiationService.createInstance( + PropertyHeader, + this.cell, + this._cellHeaderContainer, + this.notebookEditor, + { + updateInfoRendering: () => renderSourceEditor(), + checkIfModified: (_) => ({ reason: undefined }), + getFoldingState: (cell) => cell.cellFoldingState, + updateFoldingState: (cell, state) => cell.cellFoldingState = state, + unChangedLabel: 'Input', + changedLabel: this.changedLabel, + prefix: 'input', + menuId: MenuId.NotebookDiffCellInputTitle + } + )); + this._cellHeader.buildHeader(); + renderSourceEditor(); + + this._initializeSourceDiffEditor(this.nestedCellViewModel); + } + protected calculateDiagonalFillHeight() { + return this.cell.layoutInfo.cellStatusHeight + this.cell.layoutInfo.editorHeight + this.cell.layoutInfo.editorMargin + this.cell.layoutInfo.metadataStatusHeight + this.cell.layoutInfo.metadataHeight + this.cell.layoutInfo.outputTotalHeight + this.cell.layoutInfo.outputStatusHeight; + } + + private async _initializeSourceDiffEditor(modifiedCell: DiffNestedCellViewModel) { + const modifiedRef = await this.textModelService.createModelReference(modifiedCell.uri); + + if (this._isDisposed) { + return; + } + + const modifiedTextModel = modifiedRef.object.textEditorModel; + this._register(modifiedRef); + + this._editor!.setModel(modifiedTextModel); + + const editorViewState = this.cell.getSourceEditorViewState() as editorCommon.IDiffEditorViewState | null; + if (editorViewState) { + this._editor!.restoreViewState(editorViewState); + } + + const contentHeight = this._editor!.getContentHeight(); + this.cell.editorHeight = contentHeight; + const height = `${this.calculateDiagonalFillHeight()}px`; + if (this._diagonalFill!.style.height !== height) { + this._diagonalFill!.style.height = height; + } + } + _disposeMetadata() { this.cell.metadataStatusHeight = 0; this.cell.metadataHeight = 0; @@ -966,7 +1071,6 @@ abstract class SingleSideDiffElement extends AbstractElementRenderer { } } export class DeletedElement extends SingleSideDiffElement { - private _editor!: CodeEditorWidget; constructor( notebookEditor: INotebookTextDiffEditor, cell: SingleSideDiffElementViewModel, @@ -986,53 +1090,36 @@ export class DeletedElement extends SingleSideDiffElement { super(notebookEditor, cell, templateData, 'left', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService); } + get nestedCellViewModel() { + return this.cell.original!; + } + get changedLabel() { + return 'Input deleted'; + } + get isEditable() { + return true; + } + styleContainer(container: HTMLElement) { container.classList.remove('inserted'); container.classList.add('removed'); } - updateSourceEditor(): void { - const originalCell = this.cell.original!; - const lineCount = originalCell.textModel.textBuffer.getLineCount(); - const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; - - this._editor = this.templateData.sourceEditor; - this._editor.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, - height: editorHeight - }); - - this.cell.editorHeight = editorHeight; - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) { - this.cell.editorHeight = e.contentHeight; - } - })); - - this.textModelService.createModelReference(originalCell.uri).then(ref => { - if (this._isDisposed) { - return; - } - - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - this.cell.editorHeight = this._editor.getContentHeight(); - }); - } - layout(state: IDiffElementLayoutState) { DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => { if (state.editorHeight || state.outerWidth) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; this._editor.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), height: this.cell.layoutInfo.editorHeight }); } + if (state.outerWidth && this._editor) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; + this._editor.layout(); + } + if (state.metadataHeight || state.outerWidth) { this._metadataEditor?.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), @@ -1048,13 +1135,14 @@ export class DeletedElement extends SingleSideDiffElement { } if (this._diagonalFill) { - this._diagonalFill.style.height = `${this.cell.layoutInfo.totalHeight - 32}px`; + this._diagonalFill.style.height = `${this.calculateDiagonalFillHeight()}px`; } this.layoutNotebookCell(); }); } + _buildOutputRendererContainer() { if (!this._outputViewContainer) { this._outputViewContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-view-container')); @@ -1118,7 +1206,6 @@ export class DeletedElement extends SingleSideDiffElement { } export class InsertElement extends SingleSideDiffElement { - private _editor!: CodeEditorWidget; constructor( notebookEditor: INotebookTextDiffEditor, cell: SingleSideDiffElementViewModel, @@ -1136,48 +1223,21 @@ export class InsertElement extends SingleSideDiffElement { ) { super(notebookEditor, cell, templateData, 'right', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService); } + get nestedCellViewModel() { + return this.cell.modified!; + } + get changedLabel() { + return 'Input inserted'; + } + get isEditable() { + return true; + } styleContainer(container: HTMLElement): void { container.classList.remove('removed'); container.classList.add('inserted'); } - updateSourceEditor(): void { - const modifiedCell = this.cell.modified!; - const lineCount = modifiedCell.textModel.textBuffer.getLineCount(); - const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; - - this._editor = this.templateData.sourceEditor; - this._editor.layout( - { - width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, - height: editorHeight - } - ); - this._editor.updateOptions({ readOnly: false }); - this.cell.editorHeight = editorHeight; - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) { - this.cell.editorHeight = e.contentHeight; - } - })); - - this.textModelService.createModelReference(modifiedCell.uri).then(ref => { - if (this._isDisposed) { - return; - } - - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - this._editor.restoreViewState(this.cell.getSourceEditorViewState() as editorCommon.ICodeEditorViewState); - this.cell.editorHeight = this._editor.getContentHeight(); - }); - } - _buildOutputRendererContainer() { if (!this._outputViewContainer) { this._outputViewContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-view-container')); @@ -1230,12 +1290,18 @@ export class InsertElement extends SingleSideDiffElement { layout(state: IDiffElementLayoutState) { DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => { if (state.editorHeight || state.outerWidth) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; this._editor.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), height: this.cell.layoutInfo.editorHeight }); } + if (state.outerWidth && this._editor) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; + this._editor.layout(); + } + if (state.metadataHeight || state.outerWidth) { this._metadataEditor?.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), @@ -1253,7 +1319,7 @@ export class InsertElement extends SingleSideDiffElement { this.layoutNotebookCell(); if (this._diagonalFill) { - this._diagonalFill.style.height = `${this.cell.layoutInfo.editorHeight + this.cell.layoutInfo.editorMargin + this.cell.layoutInfo.metadataStatusHeight + this.cell.layoutInfo.metadataHeight + this.cell.layoutInfo.outputTotalHeight + this.cell.layoutInfo.outputStatusHeight}px`; + this._diagonalFill.style.height = `${this.calculateDiagonalFillHeight()}px`; } }); } @@ -1270,7 +1336,6 @@ export class InsertElement extends SingleSideDiffElement { export class ModifiedElement extends AbstractElementRenderer { private _editor?: DiffEditorWidget; private _editorViewStateChanged: boolean; - private _editorContainer!: HTMLElement; protected _toolbar!: ToolBar; protected _menu!: IMenu; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index d59e7471d5b..e62e75cdfbf 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -364,6 +364,7 @@ .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container .monaco-editor .monaco-editor-background, +.notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .input-header-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container .monaco-editor .monaco-editor-background, @@ -381,6 +382,7 @@ .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container .monaco-editor .monaco-editor-background, +.notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .input-header-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container .monaco-editor .monaco-editor-background, diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 1fd8eb929ca..0de4d56b511 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -73,6 +73,7 @@ export interface CellDiffSingleSideRenderTemplate extends CellDiffCommonRenderTe readonly diagonalFill: HTMLElement; readonly elementDisposables: DisposableStore; readonly cellHeaderContainer: HTMLElement; + readonly editorContainer: HTMLElement; readonly sourceEditor: CodeEditorWidget; readonly metadataHeaderContainer: HTMLElement; readonly metadataInfoContainer: HTMLElement; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index a12cb9c2fe6..61869d202d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -84,7 +84,7 @@ export class CellDiffSingleSideRenderer implements IListRenderer Date: Wed, 7 Aug 2024 12:32:35 +0200 Subject: [PATCH 700/798] SCM - do not show initial commit if the repository has been published (#225031) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 43e1e321951..e7af6f4d1db 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -4156,8 +4156,10 @@ class SCMTreeHistoryProviderDataSource extends Disposable { } // If we only have one history item that contains all the labels (current, remote, base), - // we don't need to show it, unless it is the root commit (does not have any parents). - if (historyItemsElement.length === 1 && historyItemsElement[0].parentIds.length > 0) { + // we don't need to show it, unless it is the root commit (does not have any parents) and + // the repository has not been published yet. + if (historyItemsElement.length === 1 && + (historyItemsElement[0].parentIds.length > 0 || currentHistoryItemGroup.remote)) { const currentHistoryItemGroupLabels = [ currentHistoryItemGroup.name, ...currentHistoryItemGroup.remote ? [currentHistoryItemGroup.remote.name] : [], From ed73b9d5f218db6cc2c3e31e2c89164146571ecd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 7 Aug 2024 04:04:55 -0700 Subject: [PATCH 701/798] Don't pack conpty binaries into asar Part of microsoft/vscode#224488 --- build/gulpfile.vscode.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 34d5d79fbe6..edca10c8420 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -288,6 +288,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op '**/*.node', '**/@vscode/ripgrep/bin/*', '**/node-pty/build/Release/*', + '**/node-pty/build/Release/conpty/*', '**/node-pty/lib/worker/conoutSocketWorker.js', '**/node-pty/lib/shared/conout.js', '**/*.wasm', From 91209dc8ce502a6d07fa747ace73facadb7c6f66 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 7 Aug 2024 13:28:56 +0200 Subject: [PATCH 702/798] Unexpected brackets are not colored in Light High Contrast theme (#225036) --- src/vs/editor/common/core/editorColorRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index b7c71f99721..b8c8157b26b 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -80,7 +80,7 @@ export const editorBracketHighlightingForeground4 = registerColor('editorBracket export const editorBracketHighlightingForeground5 = registerColor('editorBracketHighlight.foreground5', '#00000000', nls.localize('editorBracketHighlightForeground5', 'Foreground color of brackets (5). Requires enabling bracket pair colorization.')); export const editorBracketHighlightingForeground6 = registerColor('editorBracketHighlight.foreground6', '#00000000', nls.localize('editorBracketHighlightForeground6', 'Foreground color of brackets (6). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingUnexpectedBracketForeground = registerColor('editorBracketHighlight.unexpectedBracket.foreground', { dark: new Color(new RGBA(255, 18, 18, 0.8)), light: new Color(new RGBA(255, 18, 18, 0.8)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '' }, nls.localize('editorBracketHighlightUnexpectedBracketForeground', 'Foreground color of unexpected brackets.')); +export const editorBracketHighlightingUnexpectedBracketForeground = registerColor('editorBracketHighlight.unexpectedBracket.foreground', { dark: new Color(new RGBA(255, 18, 18, 0.8)), light: new Color(new RGBA(255, 18, 18, 0.8)), hcDark: 'new Color(new RGBA(255, 50, 50, 1))', hcLight: '#B5200D' }, nls.localize('editorBracketHighlightUnexpectedBracketForeground', 'Foreground color of unexpected brackets.')); export const editorBracketPairGuideBackground1 = registerColor('editorBracketPairGuide.background1', '#00000000', nls.localize('editorBracketPairGuide.background1', 'Background color of inactive bracket pair guides (1). Requires enabling bracket pair guides.')); export const editorBracketPairGuideBackground2 = registerColor('editorBracketPairGuide.background2', '#00000000', nls.localize('editorBracketPairGuide.background2', 'Background color of inactive bracket pair guides (2). Requires enabling bracket pair guides.')); From 16cb9de381a763d18bdd3ce1ebe55a4238bcc5fc Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 7 Aug 2024 13:32:56 +0200 Subject: [PATCH 703/798] editorOverviewRuler.findMatchForeground not set in light HC theme (#225038) --- src/vs/platform/theme/common/colors/editorColors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/theme/common/colors/editorColors.ts b/src/vs/platform/theme/common/colors/editorColors.ts index cac6dea162c..cebf9ba8f88 100644 --- a/src/vs/platform/theme/common/colors/editorColors.ts +++ b/src/vs/platform/theme/common/colors/editorColors.ts @@ -426,7 +426,7 @@ export const overviewRulerCommonContentForeground = registerColor('editorOvervie nls.localize('overviewRulerCommonContentForeground', 'Common ancestor overview ruler foreground for inline merge-conflicts.')); export const overviewRulerFindMatchForeground = registerColor('editorOverviewRuler.findMatchForeground', - { dark: '#d186167e', light: '#d186167e', hcDark: '#AB5A00', hcLight: '' }, + { dark: '#d186167e', light: '#d186167e', hcDark: '#AB5A00', hcLight: '#AB5A00' }, nls.localize('overviewRulerFindMatchForeground', 'Overview ruler marker color for find matches. The color must not be opaque so as not to hide underlying decorations.'), true); export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', From 1df84b22057f1292f51879c6d4d281c23f31ffb8 Mon Sep 17 00:00:00 2001 From: BABA <38986298+BABA983@users.noreply.github.com> Date: Wed, 7 Aug 2024 19:59:09 +0800 Subject: [PATCH 704/798] Respect the original terminal tab order after drag multiple tabs (#224591) --- .../contrib/terminal/browser/terminal.ts | 6 +- .../contrib/terminal/browser/terminalGroup.ts | 23 +++++-- .../terminal/browser/terminalGroupService.ts | 67 +++++++++++++------ .../terminal/browser/terminalService.ts | 2 +- .../terminal/browser/terminalTabsList.ts | 18 +++-- .../test/browser/workbenchTestServices.ts | 4 +- 6 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index ceda1f8b6aa..acbf92a5f0e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -138,7 +138,7 @@ export interface ITerminalGroup { attachToElement(element: HTMLElement): void; addInstance(instance: ITerminalInstance): void; removeInstance(instance: ITerminalInstance): void; - moveInstance(instance: ITerminalInstance, index: number): void; + moveInstance(instances: ITerminalInstance | ITerminalInstance[], index: number, position: 'before' | 'after'): void; setVisible(visible: boolean): void; layout(width: number, height: number): void; addDisposable(disposable: IDisposable): void; @@ -486,8 +486,8 @@ export interface ITerminalGroupService extends ITerminalInstanceHost { * @param source The source instance to move. * @param target The target instance to move the source instance to. */ - moveGroup(source: ITerminalInstance, target: ITerminalInstance): void; - moveGroupToEnd(source: ITerminalInstance): void; + moveGroup(source: ITerminalInstance | ITerminalInstance[], target: ITerminalInstance): void; + moveGroupToEnd(source: ITerminalInstance | ITerminalInstance[]): void; moveInstance(source: ITerminalInstance, target: ITerminalInstance, side: 'before' | 'after'): void; unsplitInstance(instance: ITerminalInstance): void; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 8e9ddcb5b6c..da31bcf18fd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -15,6 +15,7 @@ import { IShellLaunchConfig, ITerminalTabLayoutInfoById, TerminalLocation } from import { TerminalStatus } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { getWindow } from 'vs/base/browser/dom'; import { getPartByLocation } from 'vs/workbench/services/views/browser/viewsService'; +import { asArray } from 'vs/base/common/arrays'; const enum Constants { /** @@ -408,16 +409,24 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { } } - moveInstance(instance: ITerminalInstance, index: number): void { - const sourceIndex = this.terminalInstances.indexOf(instance); - if (sourceIndex === -1) { + moveInstance(instances: ITerminalInstance | ITerminalInstance[], index: number, position: 'before' | 'after'): void { + instances = asArray(instances); + const hasInvalidInstance = instances.some(instance => !this.terminalInstances.includes(instance)); + if (hasInvalidInstance) { return; } - this._terminalInstances.splice(sourceIndex, 1); - this._terminalInstances.splice(index, 0, instance); + const insertIndex = position === 'before' ? index : index + 1; + this._terminalInstances.splice(insertIndex, 0, ...instances); + for (const item of instances) { + const originSourceGroupIndex = position === 'after' ? this._terminalInstances.indexOf(item) : this._terminalInstances.lastIndexOf(item); + this._terminalInstances.splice(originSourceGroupIndex, 1); + } if (this._splitPaneContainer) { - this._splitPaneContainer.remove(instance); - this._splitPaneContainer.split(instance, index); + for (let i = 0; i < instances.length; i++) { + const item = instances[i]; + this._splitPaneContainer.remove(item); + this._splitPaneContainer.split(item, index + (position === 'before' ? i : 0)); + } } this._onInstancesChanged.fire(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts index 381bff3c367..ff22e6dca8b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts @@ -19,6 +19,7 @@ import { getInstanceFromResource } from 'vs/workbench/contrib/terminal/browser/t import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { asArray } from 'vs/base/common/arrays'; export class TerminalGroupService extends Disposable implements ITerminalGroupService { declare _serviceBrand: undefined; @@ -318,40 +319,66 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe this.setActiveGroupByIndex(newIndex); } - moveGroup(source: ITerminalInstance, target: ITerminalInstance) { - const sourceGroup = this.getGroupForInstance(source); - const targetGroup = this.getGroupForInstance(target); + private _getValidTerminalGroups = (sources: ITerminalInstance[]): Set => { + return new Set( + sources + .map(source => this.getGroupForInstance(source)) + .filter((group) => group !== undefined) + ); + }; - // Something went wrong - if (!sourceGroup || !targetGroup) { + moveGroup(source: ITerminalInstance | ITerminalInstance[], target: ITerminalInstance) { + source = asArray(source); + const sourceGroups = this._getValidTerminalGroups(source); + const targetGroup = this.getGroupForInstance(target); + if (!targetGroup || sourceGroups.size === 0) { return; } // The groups are the same, rearrange within the group - if (sourceGroup === targetGroup) { - const index = sourceGroup.terminalInstances.indexOf(target); - if (index !== -1) { - sourceGroup.moveInstance(source, index); - } + if (sourceGroups.size === 1 && sourceGroups.has(targetGroup)) { + const targetIndex = targetGroup.terminalInstances.indexOf(target); + const sortedSources = source.sort((a, b) => { + return targetGroup.terminalInstances.indexOf(a) - targetGroup.terminalInstances.indexOf(b); + }); + const firstTargetIndex = targetGroup.terminalInstances.indexOf(sortedSources[0]); + const position: 'before' | 'after' = firstTargetIndex < targetIndex ? 'after' : 'before'; + targetGroup.moveInstance(sortedSources, targetIndex, position); + this._onDidChangeInstances.fire(); return; } // The groups differ, rearrange groups - const sourceGroupIndex = this.groups.indexOf(sourceGroup); const targetGroupIndex = this.groups.indexOf(targetGroup); - this.groups.splice(sourceGroupIndex, 1); - this.groups.splice(targetGroupIndex, 0, sourceGroup); + const sortedSourceGroups = Array.from(sourceGroups).sort((a, b) => { + return this.groups.indexOf(a) - this.groups.indexOf(b); + }); + const firstSourceGroupIndex = this.groups.indexOf(sortedSourceGroups[0]); + const position: 'before' | 'after' = firstSourceGroupIndex < targetGroupIndex ? 'after' : 'before'; + const insertIndex = position === 'after' ? targetGroupIndex + 1 : targetGroupIndex; + this.groups.splice(insertIndex, 0, ...sortedSourceGroups); + for (const sourceGroup of sortedSourceGroups) { + const originSourceGroupIndex = position === 'after' ? this.groups.indexOf(sourceGroup) : this.groups.lastIndexOf(sourceGroup); + this.groups.splice(originSourceGroupIndex, 1); + } this._onDidChangeInstances.fire(); } - moveGroupToEnd(source: ITerminalInstance): void { - const sourceGroup = this.getGroupForInstance(source); - if (!sourceGroup) { + moveGroupToEnd(source: ITerminalInstance | ITerminalInstance[]): void { + source = asArray(source); + const sourceGroups = this._getValidTerminalGroups(source); + if (sourceGroups.size === 0) { return; } - const sourceGroupIndex = this.groups.indexOf(sourceGroup); - this.groups.splice(sourceGroupIndex, 1); - this.groups.push(sourceGroup); + const lastInstanceIndex = this.groups.length - 1; + const sortedSourceGroups = Array.from(sourceGroups).sort((a, b) => { + return this.groups.indexOf(a) - this.groups.indexOf(b); + }); + this.groups.splice(lastInstanceIndex + 1, 0, ...sortedSourceGroups); + for (const sourceGroup of sortedSourceGroups) { + const sourceGroupIndex = this.groups.indexOf(sourceGroup); + this.groups.splice(sourceGroupIndex, 1); + } this._onDidChangeInstances.fire(); } @@ -371,7 +398,7 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe // Rearrange within the target group const index = targetGroup.terminalInstances.indexOf(target) + (side === 'after' ? 1 : 0); - targetGroup.moveInstance(source, index); + targetGroup.moveInstance(source, index, side); } unsplitInstance(instance: ITerminalInstance) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 7247bd7cc64..2dc85842d94 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -823,7 +823,7 @@ export class TerminalService extends Disposable implements ITerminalService { if (target && side) { const index = group.terminalInstances.indexOf(target) + (side === 'after' ? 1 : 0); - group.moveInstance(source, index); + group.moveInstance(source, index, side); } // Fire events diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 86824099a3b..254e4cb11de 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -668,7 +668,11 @@ class TerminalTabsDragAndDrop extends Disposable implements IListDragAndDrop Date: Wed, 7 Aug 2024 05:32:08 -0700 Subject: [PATCH 705/798] Add all pwsh keywords as completions Fixes #225042 --- .../browser/media/shellIntegration.ps1 | 115 +++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 index 58438e4f12f..d6a560ba6f6 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 @@ -170,6 +170,13 @@ function Set-MappedKeyHandler { } } +function Get-KeywordCompletionResult( + $Keyword, + $Description = $null +) { + [System.Management.Automation.CompletionResult]::new($Keyword, $Keyword, [System.Management.Automation.CompletionResultType]::Keyword, $null -ne $Description ? $Description : $Keyword) +} + function Set-MappedKeyHandlers { Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a' Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b' @@ -191,8 +198,112 @@ function Set-MappedKeyHandlers { # Get commands, convert to string array to reduce the payload size and send as JSON $commands = @( [System.Management.Automation.CompletionCompleters]::CompleteCommand('') - # Keywords aren't included in CompletionCommand - [System.Management.Automation.CompletionResult]::new('exit', 'exit', [System.Management.Automation.CompletionResultType]::Keyword, "exit []") + Get-KeywordCompletionResult -Keyword 'begin' + Get-KeywordCompletionResult -Keyword 'break' + Get-KeywordCompletionResult -Keyword 'catch' -Description "catch [[][',' ]*] {}" + Get-KeywordCompletionResult -Keyword 'class' -Description @" +class [: [][,]] { + [[] [hidden] [static] ...] + [([]) + {} ...] + [[] [hidden] [static] ...] +} +"@ + Get-KeywordCompletionResult -Keyword 'clean' + Get-KeywordCompletionResult -Keyword 'continue' + Get-KeywordCompletionResult -Keyword 'data' -Description @" +data [] [-supportedCommand ] { + +} +"@ + Get-KeywordCompletionResult -Keyword 'do' -Description @" +do {} while () +do {} until () +"@ + Get-KeywordCompletionResult -Keyword 'dynamicparam' -Description "dynamicparam {}" + Get-KeywordCompletionResult -Keyword 'else' -Description @" +if () + {} +[elseif () + {}] +[else + {}] +"@ + Get-KeywordCompletionResult -Keyword 'elseif' -Description @" +if () + {} +[elseif () + {}] +[else + {}] +"@ + Get-KeywordCompletionResult -Keyword 'end' + Get-KeywordCompletionResult -Keyword 'enum' -Description @" +[[]...] [Flag()] enum [ : ] { +